diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h index 3635ee5a537a..f2f23766f229 100644 --- a/lldb/include/lldb/API/SBFrame.h +++ b/lldb/include/lldb/API/SBFrame.h @@ -122,6 +122,9 @@ class LLDB_API SBFrame { lldb::SBValue EvaluateExpression(const char *expr, const SBExpressionOptions &options); + lldb::SBValue EvaluateExpressionViaDIL(const char *expr, + lldb::DynamicValueType use_dynamic); + /// Language plugins can use this API to report language-specific /// runtime information about this compile unit, such as additional /// language version details or feature flags. diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp index 2300bec4d685..79eeaa4356ce 100644 --- a/lldb/source/API/SBFrame.cpp +++ b/lldb/source/API/SBFrame.cpp @@ -1155,6 +1155,82 @@ lldb::SBValue SBFrame::EvaluateExpression(const char *expr, return expr_result; } +lldb::SBValue +SBFrame::EvaluateExpressionViaDIL(const char *expr, + lldb::DynamicValueType use_dynamic) { + LLDB_INSTRUMENT_VA(this, expr); + Log *expr_log = GetLog(LLDBLog::Expressions); + SBValue expr_result; + if (expr == nullptr || expr[0] == '\0') { + return expr_result; + } + ValueObjectSP expr_value_sp; + std::unique_lock lock; + ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + StackFrame *frame = nullptr; + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + SBExpressionOptions options; + options.SetFetchDynamicValue(use_dynamic); + if (target && process) { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process->GetRunLock())) { + frame = exe_ctx.GetFramePtr(); + if (frame) { + std::unique_ptr stack_trace; + if (target->GetDisplayExpressionsInCrashlogs()) { + StreamString frame_description; + frame->DumpUsingSettingsFormat(&frame_description); + stack_trace = std::make_unique( + "SBFrame::EvaluateExpressionViaDIL (expr = \"%s\", " + "fetch_dynamic_value " + "= %u) %s", + expr, options.GetFetchDynamicValue(), + frame_description.GetData()); + } + VariableSP var_sp; + Status error; + expr_value_sp = + frame->GetValueForVariableExpressionPath( // DIL in ths branch + expr, use_dynamic, + StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, + var_sp, error); + if (!error.Success()) { + expr_value_sp = + ValueObjectConstResult::Create(nullptr, std::move(error)); + expr_result.SetSP(expr_value_sp, false); + } else { + expr_result.SetSP(expr_value_sp, options.GetFetchDynamicValue()); + } + } + } else { + Status error; + error = Status::FromErrorString("can't evaluate expressions when the " + "process is running."); + expr_value_sp = ValueObjectConstResult::Create(nullptr, std::move(error)); + expr_result.SetSP(expr_value_sp, false); + } + } else { + Status error; + error = Status::FromErrorString("sbframe object is not valid."); + expr_value_sp = ValueObjectConstResult::Create(nullptr, std::move(error)); + expr_result.SetSP(expr_value_sp, false); + } + if (expr_result.GetError().Success()) + LLDB_LOGF(expr_log, + "** [SBFrame::EvaluateExpressionViaDIL] Expression result is " + "%s, summary %s **", + expr_result.GetValue(), expr_result.GetSummary()); + else + LLDB_LOGF( + expr_log, + "** [SBFrame::EvaluateExpressionViaDIL] Expression evaluation failed: " + "%s **", + expr_result.GetError().GetCString()); + return expr_result; +} + SBStructuredData SBFrame::GetLanguageSpecificData() const { LLDB_INSTRUMENT_VA(this); diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt index cc9d45ebf981..d19b9dfb4c1a 100644 --- a/lldb/unittests/CMakeLists.txt +++ b/lldb/unittests/CMakeLists.txt @@ -55,6 +55,7 @@ add_subdirectory(Breakpoint) add_subdirectory(Callback) add_subdirectory(Core) add_subdirectory(DataFormatter) +add_subdirectory(DIL) add_subdirectory(Disassembler) add_subdirectory(Editline) add_subdirectory(Expression) diff --git a/lldb/unittests/DIL/CMakeLists.txt b/lldb/unittests/DIL/CMakeLists.txt new file mode 100644 index 000000000000..454aa5208fb4 --- /dev/null +++ b/lldb/unittests/DIL/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_unittest(DILTests + DILTests.cpp + Runner.cpp + + LINK_LIBS + liblldb + lldbUtilityHelpers + LLVMTestingSupport + ) +add_subdirectory(Inputs) +add_dependencies(DILTests test_binary) + +add_unittest_inputs(DILTests "test_binary.cc") diff --git a/lldb/unittests/DIL/DILTests.cpp b/lldb/unittests/DIL/DILTests.cpp new file mode 100644 index 000000000000..4fa2440fc234 --- /dev/null +++ b/lldb/unittests/DIL/DILTests.cpp @@ -0,0 +1,3703 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// DIL TODO: +// * Fix errors in XFailed tests +// * Fix segfault errors in GTEST_SKIP tests +// * Implement interfaces for scoped, context, and separate parsing +// and re-enable DISABLED_ tests + +#include "lldb/API/SBError.h" +#include "lldb/lldb-enumerations.h" +#ifndef __EMSCRIPTEN__ +#include +#include +#include +#include +#include +#include + +#include "TestingSupport/TestUtilities.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBType.h" +#include "Runner.h" +#include "Traits.h" +#endif + +// DISALLOW_COPY_AND_ASSIGN is also defined in +// lldb/lldb-defines.h +#undef DISALLOW_COPY_AND_ASSIGN +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#ifndef __EMSCRIPTEN__ + +using ::testing::MakeMatcher; +using ::testing::Matcher; +using ::testing::MatcherInterface; +using ::testing::MatchResultListener; + +struct EvalResult { + lldb::SBError lldb_DIL_error; + mutable lldb::SBValue lldb_DIL_value; + mutable std::optional lldb_value; + + friend std::ostream &operator<<(std::ostream &os, const EvalResult &result) { + auto maybe_null = [](const char *str) { + return str == nullptr ? "NULL" : str; + }; + + os << "{ DIL: " << maybe_null(result.lldb_DIL_value.GetValue()); + + if (result.lldb_value.has_value()) { + os << ", lldb: " << maybe_null(result.lldb_value.value().GetValue()); + } + os << " }"; + + return os; + } +}; + +struct CompiledExpr { + // Unsupported +}; + +class EvaluatorHelper { +public: + EvaluatorHelper(lldb::SBFrame frame, bool lldb, bool side_effects) + : frame_(frame), lldb_(lldb) {} + EvaluatorHelper(lldb::SBValue scope, bool lldb, bool side_effects) + : scope_(scope), lldb_(lldb) {} + +public: + EvalResult Eval(const std::string &expr) { + EvalResult ret; + + // lldb_eval::Options opts; + // opts.allow_side_effects = side_effects_; + + if (scope_) { + ret.lldb_DIL_error = + lldb::SBError("Scope evaluation is not yet supported"); + // // Evaluate in the variable context. + // ret.lldb_DIL_value = lldb_eval::EvaluateExpression( + // scope_, expr.c_str(), opts, ret.lldb_DIL_error); + + if (lldb_) { + ret.lldb_value = scope_.EvaluateExpression(expr.c_str()); + } + } else { + // Evaluate in the frame context. + ret.lldb_DIL_value = + frame_.EvaluateExpressionViaDIL(expr.c_str(), lldb::eNoDynamicValues); + if (!ret.lldb_DIL_value.GetError().Success()) + ret.lldb_DIL_error = ret.lldb_DIL_value.GetError(); + // ret.lldb_DIL_value = lldb_eval::EvaluateExpression( + // frame_, expr.c_str(), opts, ret.lldb_DIL_error); + + if (lldb_) { + ret.lldb_value = frame_.EvaluateExpression(expr.c_str()); + } + } + + return ret; + } + + EvalResult Eval(CompiledExpr compiled_expr) { + assert(scope_ && "compiled expression requires a value context"); + + EvalResult ret; + ret.lldb_DIL_error = lldb::SBError("Separate parsing is not yet supported"); + // ret.lldb_DIL_value = lldb_eval::EvaluateExpression(scope_, compiled_expr, + // ret.lldb_DIL_error); + return ret; + } + + EvalResult + EvalWithContext(const std::string &expr, + const std::unordered_map &vars) { + EvalResult ret; + + // std::vector ctx_vec; + // ctx_vec.reserve(vars.size()); + // for (const auto& [name, value] : vars) { + // ctx_vec.push_back({name.c_str(), value}); + // } + + // lldb_eval::Options opts; + // opts.allow_side_effects = side_effects_; + // opts.context_vars = {ctx_vec.data(), ctx_vec.size()}; + + if (scope_) { + // Evaluate in the variable context. + ret.lldb_DIL_error = + lldb::SBError("Evaluation with context is not yet supported"); + // ret.lldb_DIL_value = lldb_eval::EvaluateExpression( + // scope_, expr.c_str(), opts, ret.lldb_DIL_error); + + if (lldb_) { + ret.lldb_value = scope_.EvaluateExpression(expr.c_str()); + } + } else { + // Evaluate in the frame context. + ret.lldb_DIL_error = + lldb::SBError("Evaluation with context is not yet supported"); + // ret.lldb_DIL_value = lldb_eval::EvaluateExpression( + // frame_, expr.c_str(), opts, ret.lldb_DIL_error); + + if (lldb_) { + ret.lldb_value = frame_.EvaluateExpression(expr.c_str()); + } + } + + return ret; + } + + EvalResult + EvalWithContext(CompiledExpr compiled_expr, + const std::unordered_map &vars) { + assert(scope_ && "compiled expression requires a value context"); + + EvalResult ret; + ret.lldb_DIL_error = lldb::SBError("Separate parsing is not yet supported"); + return ret; + + // std::vector ctx_vec; + // ctx_vec.reserve(vars.size()); + // for (const auto& [name, value] : vars) { + // ctx_vec.push_back({name.c_str(), value}); + // } + // lldb_eval::ContextVariableList context_vars{ctx_vec.data(), + // ctx_vec.size()}; + + // EvalResult ret; + // ret.lldb_DIL_value = lldb_eval::EvaluateExpression( + // scope_, compiled_expr, context_vars, ret.lldb_DIL_error); + // return ret; + } + + CompiledExpr Compile(const std::string &expr, lldb::SBError &error) { + assert(scope_ && "compiling an expression requires a type context"); + + error = lldb::SBError("Separate parsing is not yet supported"); + return CompiledExpr(); + // lldb_eval::Options opts; + // opts.allow_side_effects = side_effects_; + // return lldb_eval::CompileExpression(scope_.GetTarget(), scope_.GetType(), + // expr.c_str(), opts, error); + } + + CompiledExpr + CompileWithContext(const std::string &expr, + const std::unordered_map &args, + lldb::SBError &error) { + assert(scope_ && "compiling an expression requires a type context"); + + error = lldb::SBError("Separate parsing is not yet supported"); + return CompiledExpr(); + + // std::vector ctx_vec; + // ctx_vec.reserve(args.size()); + // for (const auto& [name, type] : args) { + // ctx_vec.push_back({name.c_str(), type}); + // } + + // lldb_eval::Options opts; + // opts.allow_side_effects = side_effects_; + // opts.context_args = {ctx_vec.data(), ctx_vec.size()}; + + // return lldb_eval::CompileExpression(scope_.GetTarget(), scope_.GetType(), + // expr.c_str(), opts, error); + } + +private: + lldb::SBFrame frame_; + lldb::SBValue scope_; + bool lldb_; + // bool side_effects_; +}; +#endif + +void PrintError(::testing::MatchResultListener *listener, + const std::string &error) { + *listener << "error:"; + // Print multiline errors on a separate line. + if (error.find('\n') != std::string::npos) { + *listener << "\n"; + } else { + *listener << " "; + } + *listener << error; +} + +#ifndef __EMSCRIPTEN__ +class IsOkMatcher : public MatcherInterface { +public: + explicit IsOkMatcher(bool compare_types) : compare_types_(compare_types) {} + + bool MatchAndExplain(EvalResult result, + MatchResultListener *listener) const override { + if (result.lldb_DIL_error.GetError()) { + PrintError(listener, result.lldb_DIL_error.GetCString()); + return false; + } + + std::string actual = result.lldb_DIL_value.GetValue(); + // Compare only if we tried to evaluate with LLDB. + if (result.lldb_value.has_value()) { + if (result.lldb_value.value().GetError().GetError()) { + *listener << "values produced by DIL and LLDB don't match\n" + << "DIL: " << actual << "\n" + << "lldb : " + << result.lldb_value.value().GetError().GetCString(); + return false; + + } else if (actual != result.lldb_value.value().GetValue()) { + *listener << "values produced by DIL and LLDB don't match\n" + << "DIL: " << actual << "\n" + << "lldb : " << result.lldb_value.value().GetValue(); + return false; + } + + if (compare_types_) { + const char *lldb_DIL_type = + result.lldb_DIL_value.GetType().GetUnqualifiedType().GetName(); + const char *lldb_type = + result.lldb_value.value().GetType().GetUnqualifiedType().GetName(); + if (strcmp(lldb_DIL_type, lldb_type) != 0) { + *listener << "types produced by DIL and LLDB don't match\n" + << "DIL: " << lldb_DIL_type << "\n" + << "lldb : " << lldb_type; + return false; + } + } + } + + return true; + } + + void DescribeTo(std::ostream *os) const override { + *os << "evaluates without an error and equals to LLDB"; + } + +private: + bool compare_types_; +}; +#endif + +Matcher IsOk(bool compare_types = true) { + return MakeMatcher(new IsOkMatcher(compare_types)); +} + +#ifndef __EMSCRIPTEN__ +class IsEqualMatcher : public MatcherInterface { +public: + IsEqualMatcher(std::string value, bool compare_types) + : value_(std::move(value)), compare_types_(compare_types) {} + +public: + bool MatchAndExplain(EvalResult result, + MatchResultListener *listener) const override { + if (result.lldb_DIL_error.GetError()) { + PrintError(listener, result.lldb_DIL_error.GetCString()); + return false; + } + + std::string actual = result.lldb_DIL_value.GetValue(); + if (actual != value_) { + *listener << "evaluated to '" << actual << "'"; + return false; + } + + // Compare only if we tried to evaluate with LLDB. + if (result.lldb_value.has_value()) { + if (result.lldb_value.value().GetError().GetError()) { + *listener << "values produced by DIL and LLDB don't match\n" + << "DIL: " << actual << "\n" + << "lldb : " + << result.lldb_value.value().GetError().GetCString(); + return false; + + } else if (actual != result.lldb_value.value().GetValue()) { + *listener << "values produced by DIL and LLDB don't match\n" + << "DIL: " << actual << "\n" + << "lldb : " << result.lldb_value.value().GetValue(); + return false; + } + + if (compare_types_) { + const char *lldb_DIL_type = + result.lldb_DIL_value.GetType().GetUnqualifiedType().GetName(); + const char *lldb_type = + result.lldb_value.value().GetType().GetUnqualifiedType().GetName(); + if (strcmp(lldb_DIL_type, lldb_type) != 0) { + *listener << "types produced by DIL and LLDB don't match\n" + << "DIL: " << lldb_DIL_type << "\n" + << "lldb : " << lldb_type; + return false; + } + } + } + + return true; + } + + void DescribeTo(std::ostream *os) const override { + *os << "evaluates to '" << value_ << "'"; + } + +private: + std::string value_; + bool compare_types_; +}; +#endif + +Matcher IsEqual(std::string value, bool compare_types = true) { + return MakeMatcher(new IsEqualMatcher(std::move(value), compare_types)); +} + +#ifndef __EMSCRIPTEN__ +class IsErrorMatcher : public MatcherInterface { +public: + explicit IsErrorMatcher(std::string value) : value_(std::move(value)) {} + +public: + bool MatchAndExplain(EvalResult result, + MatchResultListener *listener) const override { + if (!result.lldb_DIL_error.GetError()) { + *listener << "evaluated to '" << result.lldb_DIL_value.GetValue() << "'"; + return false; + } + std::string message = result.lldb_DIL_error.GetCString(); + if (message.find(value_) == std::string::npos) { + PrintError(listener, message); + return false; + } + + return true; + } + + void DescribeTo(std::ostream *os) const override { + *os << "evaluates with an error: '" << value_ << "'"; + } + +private: + std::string value_; +}; +#endif + +Matcher IsError(std::string value) { + return MakeMatcher(new IsErrorMatcher(std::move(value))); +} + +static unsigned xfailed = 0; +template Matcher XFail(InnerMatcher m) { + xfailed++; + return Not(m); +} + +#ifndef __EMSCRIPTEN__ +class EvalTest : public ::testing::Test { +protected: + static void SetUpTestSuite() { lldb::SBDebugger::Initialize(); } + + static void TearDownTestSuite() { lldb::SBDebugger::Terminate(); } + + void SetUp() { + std::string test_name = + ::testing::UnitTest::GetInstance()->current_test_info()->name(); + std::string break_line = "// BREAK(" + test_name + ")"; + + std::string binary_path = + lldb_private::GetInputFilePath("test_binary.bin"); + std::string source_path = lldb_private::GetInputFilePath("test_binary.cc"); + + debugger_ = lldb::SBDebugger::Create(false); + process_ = + LaunchTestProgram(debugger_, source_path, binary_path, break_line); + frame_ = process_.GetSelectedThread().GetSelectedFrame(); + } + + void TearDown() { + if (xfailed != 0) { + printf("[ !! ] XFailed test cases: %d\n", xfailed); + xfailed = 0; + } + process_.Destroy(); + lldb::SBDebugger::Destroy(debugger_); + } + + EvalResult Eval(const std::string &expr) { + return EvaluatorHelper(frame_, compare_with_lldb_, allow_side_effects_) + .Eval(expr); + } + + EvalResult + EvalWithContext(const std::string &expr, + const std::unordered_map &vars) { + return EvaluatorHelper(frame_, compare_with_lldb_, allow_side_effects_) + .EvalWithContext(expr, vars); + } + + EvaluatorHelper Scope(std::string scope) { + // Resolve the scope variable (assume it's a local variable). + lldb::SBValue scope_var = frame_.FindVariable(scope.c_str()); + return EvaluatorHelper(scope_var, compare_with_lldb_, allow_side_effects_); + } + + bool CreateContextVariable(std::string type, std::string name, bool is_array, + std::string assignment) { + std::string expr = type + " " + name + (is_array ? "[]" : "") + " = " + + assignment + "; " + name; + lldb::SBValue value = frame_.EvaluateExpression(expr.c_str()); + if (value.GetError().Fail()) { + return false; + } + vars_.emplace(name, value); + return true; + } + + bool CreateContextVariable(std::string name, std::string assignment) { + return CreateContextVariable("auto", name, false, assignment); + } + + bool CreateContextVariableArray(std::string type, std::string name, + std::string assignment) { + return CreateContextVariable(type, name, true, assignment); + } + + bool Is32Bit() const { + if (process_.GetAddressByteSize() == 4) { + return true; + } + return false; + } + +protected: + lldb::SBDebugger debugger_; + lldb::SBProcess process_; + lldb::SBFrame frame_; + + // Evaluate with both DIL and LLDB by default. + bool compare_with_lldb_ = true; + + // Allow the expressions to have side-effects. + bool allow_side_effects_ = false; + + // Context variables. + std::unordered_map vars_; +}; + +TEST_F(EvalTest, TestSymbols) { + EXPECT_GT(frame_.GetModule().GetNumSymbols(), (size_t)0) + << "No symbols might indicate that the test binary was built incorrectly"; +} + +#endif + +TEST_F(EvalTest, TestArithmetic) { + EXPECT_THAT(Eval("1 + 2"), IsEqual("3")); + EXPECT_THAT(Eval("1 + 2*3"), IsEqual("7")); + EXPECT_THAT(Eval("1 + (2 - 3)"), IsEqual("0")); + EXPECT_THAT(Eval("1 == 2"), IsEqual("false")); + EXPECT_THAT(Eval("1 == 1"), IsEqual("true")); + + // Note: Signed overflow is UB. + EXPECT_THAT(Eval("int_max + 1"), IsOk()); + EXPECT_THAT(Eval("int_min - 1"), IsOk()); + EXPECT_THAT(Eval("2147483647 + 1"), IsOk()); + EXPECT_THAT(Eval("-2147483648 - 1"), IsOk()); + + EXPECT_THAT(Eval("uint_max + 1"), IsEqual("0")); + EXPECT_THAT(Eval("uint_zero - 1"), IsEqual("4294967295")); + EXPECT_THAT(Eval("4294967295 + 1"), IsEqual("4294967296")); + EXPECT_THAT(Eval("4294967295U + 1"), IsEqual("0")); + + // Note: Signed overflow is UB. + EXPECT_THAT(Eval("ll_max + 1"), IsOk()); + EXPECT_THAT(Eval("ll_min - 1"), IsOk()); + EXPECT_THAT(Eval("9223372036854775807 + 1"), IsOk()); + EXPECT_THAT(Eval("-9223372036854775808 - 1"), IsOk()); + + EXPECT_THAT(Eval("ull_max + 1"), IsEqual("0")); + EXPECT_THAT(Eval("ull_zero - 1"), IsEqual("18446744073709551615")); + EXPECT_THAT(Eval("9223372036854775807 + 1"), IsEqual("-9223372036854775808")); + EXPECT_THAT(Eval("9223372036854775807LL + 1"), + IsEqual("-9223372036854775808")); + EXPECT_THAT(Eval("18446744073709551615ULL + 1"), IsEqual("0")); + + // Integer literal is too large to be represented in a signed integer type, + // interpreting as unsigned. + EXPECT_THAT(Eval("-9223372036854775808"), IsEqual("9223372036854775808")); + EXPECT_THAT(Eval("-9223372036854775808 - 1"), IsEqual("9223372036854775807")); + EXPECT_THAT(Eval("-9223372036854775808 + 1"), IsEqual("9223372036854775809")); + EXPECT_THAT(Eval("-9223372036854775808LL / -1"), IsEqual("0")); + EXPECT_THAT(Eval("-9223372036854775808LL % -1"), + IsEqual("9223372036854775808")); + + EXPECT_THAT(Eval("-20 / 1U"), IsEqual("4294967276")); + EXPECT_THAT(Eval("-20LL / 1U"), IsEqual("-20")); + EXPECT_THAT(Eval("-20LL / 1ULL"), IsEqual("18446744073709551596")); + + // Unary arithmetic. + EXPECT_THAT(Eval("+0"), IsEqual("0")); + EXPECT_THAT(Eval("-0"), IsEqual("0")); + EXPECT_THAT(Eval("+1"), IsEqual("1")); + EXPECT_THAT(Eval("-1"), IsEqual("-1")); + EXPECT_THAT(Eval("c"), IsEqual("'\\n'")); + EXPECT_THAT(Eval("+c"), IsEqual("10")); + EXPECT_THAT(Eval("-c"), IsEqual("-10")); + EXPECT_THAT(Eval("uc"), IsEqual("'\\x01'")); + EXPECT_THAT(Eval("-uc"), IsEqual("-1")); + EXPECT_THAT(Eval("+p"), IsOk()); + EXPECT_THAT(Eval("-p"), + IsError("invalid argument type 'int *' to unary expression")); + + // Floating tricks. + EXPECT_THAT(Eval("+0.0"), IsEqual("0")); + EXPECT_THAT(Eval("-0.0"), IsEqual("-0")); + EXPECT_THAT(Eval("0.0 / 0"), IsEqual("NaN")); + EXPECT_THAT(Eval("0 / 0.0"), IsEqual("NaN")); + EXPECT_THAT(Eval("1 / +0.0"), IsEqual("+Inf")); + EXPECT_THAT(Eval("1 / -0.0"), IsEqual("-Inf")); + EXPECT_THAT(Eval("+0.0 / +0.0 != +0.0 / +0.0"), IsEqual("true")); + EXPECT_THAT(Eval("-1.f * 0"), IsEqual("-0")); + EXPECT_THAT(Eval("2. + .5"), IsEqual("2.5")); + EXPECT_THAT(Eval("1e2"), IsEqual("100")); + EXPECT_THAT(Eval("1e+2"), IsEqual("100")); + EXPECT_THAT(Eval(".12e+1"), IsEqual("1.2")); + EXPECT_THAT(Eval("0x0.123p-1"), IsEqual("0.0355224609375")); + + EXPECT_THAT(Eval("fnan < fnan"), IsEqual("false")); + EXPECT_THAT(Eval("fnan <= fnan"), IsEqual("false")); + EXPECT_THAT(Eval("fnan == fnan"), IsEqual("false")); + EXPECT_THAT(Eval("(unsigned int) fdenorm"), IsEqual("0")); + EXPECT_THAT(Eval("(unsigned int) (1.0f + fdenorm)"), IsEqual("1")); + + // Invalid remainder. + EXPECT_THAT( + Eval("1.1 % 2"), + IsError("invalid operands to binary expression ('double' and 'int')")); + + // References and typedefs. + EXPECT_THAT(Eval("r + 1"), IsEqual("3")); + EXPECT_THAT(Eval("r - 1l"), IsEqual("1")); + EXPECT_THAT(Eval("r * 2u"), IsEqual("4")); + EXPECT_THAT(Eval("r / 2ull"), IsEqual("1")); + EXPECT_THAT(Eval("my_r + 1"), IsEqual("3")); + EXPECT_THAT(Eval("my_r - 1"), IsEqual("1")); + EXPECT_THAT(Eval("my_r * 2"), IsEqual("4")); + EXPECT_THAT(Eval("my_r / 2"), IsEqual("1")); + EXPECT_THAT(Eval("r + my_r"), IsEqual("4")); + EXPECT_THAT(Eval("r - my_r"), IsEqual("0")); + EXPECT_THAT(Eval("r * my_r"), IsEqual("4")); + EXPECT_THAT(Eval("r / my_r"), IsEqual("1")); + + // Some promotions and conversions. + EXPECT_THAT(Eval("(uint8_t)250 + (uint8_t)250"), IsEqual("500")); + + // Makes sure that the expression isn't parsed as two types `r` and `r`. + EXPECT_THAT(Eval("(r < r > r)"), IsEqual("false")); + + // On Windows sizeof(int) == sizeof(long) == 4. + if constexpr (sizeof(int) == sizeof(long)) { + EXPECT_THAT(Eval("(unsigned int)4294967295 + (long)2"), IsEqual("1")); + EXPECT_THAT(Eval("((unsigned int)1 + (long)1) - 3"), IsEqual("4294967295")); + } else { + // On Linux sizeof(int) == 4 and sizeof(long) == 8. + EXPECT_THAT(Eval("(unsigned int)4294967295 + (long)2"), + IsEqual("4294967297")); + EXPECT_THAT(Eval("((unsigned int)1 + (long)1) - 3"), IsEqual("-1")); + } +} + +TEST_F(EvalTest, TestZeroDivision) { + // Zero division and remainder is UB and LLDB return garbage values. + // DIL implementation detects division by zero. + this->compare_with_lldb_ = false; + + EXPECT_THAT(Eval("1 / 0"), IsError("Division by zero detected.")); + EXPECT_THAT(Eval("1 / uint_zero"), IsError("Division by zero detected.")); + EXPECT_THAT(Eval("1ll / 0 + 1"), IsError("Division by zero detected.")); + + EXPECT_THAT(Eval("1 % 0"), IsError("Division by zero detected.")); + EXPECT_THAT(Eval("1 % uint_zero"), IsError("Division by zero detected.")); + EXPECT_THAT(Eval("1 % uint_zero + 1"), IsError("Division by zero detected.")); +} + +TEST_F(EvalTest, TestBitwiseOperators) { + EXPECT_THAT(Eval("~(-1)"), IsEqual("0")); + EXPECT_THAT(Eval("~~0"), IsEqual("0")); + EXPECT_THAT(Eval("~0"), IsEqual("-1")); + EXPECT_THAT(Eval("~1"), IsEqual("-2")); + EXPECT_THAT(Eval("~0LL"), IsEqual("-1")); + EXPECT_THAT(Eval("~1LL"), IsEqual("-2")); + EXPECT_THAT(Eval("~true"), IsEqual("-2")); + EXPECT_THAT(Eval("~false"), IsEqual("-1")); + EXPECT_THAT(Eval("~var_true"), IsEqual("-2")); + EXPECT_THAT(Eval("~var_false"), IsEqual("-1")); + EXPECT_THAT(Eval("~ull_max"), IsEqual("0")); + EXPECT_THAT(Eval("~ull_zero"), IsEqual("18446744073709551615")); + + EXPECT_THAT(Eval("~s"), + IsError("invalid argument type 'S' to unary expression")); + EXPECT_THAT( + Eval("~p"), + IsError("invalid argument type 'const char *' to unary expression")); + + EXPECT_THAT(Eval("(1 << 5)"), IsEqual("32")); + EXPECT_THAT(Eval("(32 >> 2)"), IsEqual("8")); + EXPECT_THAT(Eval("(-1 >> 10)"), IsEqual("-1")); + EXPECT_THAT(Eval("(-100 >> 5)"), IsEqual("-4")); + EXPECT_THAT(Eval("(-3 << 6)"), IsEqual("-192")); + EXPECT_THAT(Eval("(2000000000U << 1)"), IsEqual("4000000000")); + EXPECT_THAT(Eval("(-1 >> 1U)"), IsEqual("-1")); + EXPECT_THAT(Eval("(char)1 << 16"), IsEqual("65536")); + EXPECT_THAT(Eval("(signed char)-123 >> 8"), IsEqual("-1")); + + EXPECT_THAT(Eval("0b1011 & 0xFF"), IsEqual("11")); + EXPECT_THAT(Eval("0b1011 & mask_ff"), XFail(IsEqual("11"))); + EXPECT_THAT(Eval("0b1011 & 0b0111"), IsEqual("3")); + EXPECT_THAT(Eval("0b1011 | 0b0111"), IsEqual("15")); + EXPECT_THAT(Eval("-0b1011 | 0xFF"), IsEqual("-1")); + EXPECT_THAT(Eval("-0b1011 | 0xFFu"), IsEqual("4294967295")); + EXPECT_THAT(Eval("0b1011 ^ 0b0111"), IsEqual("12")); + EXPECT_THAT(Eval("~0b1011"), IsEqual("-12")); +} + +TEST_F(EvalTest, TestPointerArithmetic) { + EXPECT_THAT(Eval("p_char1"), IsOk()); + EXPECT_THAT(Eval("p_char1 + 1"), IsOk()); + EXPECT_THAT(Eval("p_char1 + offset"), IsOk()); + + EXPECT_THAT(Eval("my_p_char1"), IsOk()); + EXPECT_THAT(Eval("my_p_char1 + 1"), IsOk()); + EXPECT_THAT(Eval("my_p_char1 + offset"), IsOk()); + + EXPECT_THAT(Eval("*(p_char1 + 0)"), IsEqual("'h'")); + EXPECT_THAT(Eval("*(1 + p_char1)"), IsEqual("'e'")); + EXPECT_THAT(Eval("*(p_char1 + 2)"), IsEqual("'l'")); + EXPECT_THAT(Eval("*(3 + p_char1)"), IsEqual("'l'")); + EXPECT_THAT(Eval("*(p_char1 + 4)"), IsEqual("'o'")); + EXPECT_THAT(Eval("*(p_char1 + offset - 1)"), IsEqual("'o'")); + + EXPECT_THAT(Eval("*p_int0"), IsEqual("0")); + EXPECT_THAT(Eval("*cp_int5"), IsEqual("5")); + EXPECT_THAT(Eval("*(&*(cp_int5 + 1) - 1)"), IsEqual("5")); + + EXPECT_THAT(Eval("p_int0 - p_int0"), IsEqual("0")); + EXPECT_THAT(Eval("cp_int5 - p_int0"), IsEqual("5")); + EXPECT_THAT(Eval("cp_int5 - td_int_ptr0"), IsEqual("5")); + EXPECT_THAT(Eval("td_int_ptr0 - cp_int5"), IsEqual("-5")); + + EXPECT_THAT( + Eval("-p_char1"), + IsError("invalid argument type 'const char *' to unary expression")); + EXPECT_THAT(Eval("cp_int5 - p_char1"), + IsError("'const int *' and 'const char *' are not pointers to " + "compatible types")); + EXPECT_THAT(Eval("p_int0 + cp_int5"), + IsError("invalid operands to binary expression ('int *' and " + "'const int *')")); + EXPECT_THAT(Eval("p_int0 > p_char1"), + IsError("comparison of distinct pointer types ('int *' and " + "'const char *')")); + + EXPECT_THAT(Eval("cp_int5 > td_int_ptr0"), IsEqual("true")); + EXPECT_THAT(Eval("cp_int5 < td_int_ptr0"), IsEqual("false")); + EXPECT_THAT(Eval("cp_int5 != td_int_ptr0"), IsEqual("true")); + EXPECT_THAT(Eval("cp_int5 == td_int_ptr0 + offset"), IsEqual("true")); + + EXPECT_THAT(Eval("p_void + 1"), IsError("arithmetic on a pointer to void")); + EXPECT_THAT(Eval("p_void - 1"), IsError("arithmetic on a pointer to void")); + EXPECT_THAT(Eval("p_void - p_char1"), + IsError("'void *' and 'const char *' are not pointers to " + "compatible types")); + EXPECT_THAT(Eval("p_void - p_void"), + IsError("arithmetic on pointers to void")); + + EXPECT_THAT(Eval("pp_void0 - p_char1"), + IsError("'void **' and 'const char *' are not pointers to " + "compatible types")); + EXPECT_THAT(Eval("pp_void0 == p_char1"), + IsError("comparison of distinct pointer types ('void **' and " + "'const char *')")); + + EXPECT_THAT(Eval("+array"), IsOk()); + EXPECT_THAT(Eval("+array_ref"), IsOk()); + EXPECT_THAT(Eval("-array"), + IsError("invalid argument type 'int *' to unary expression\n" + "-array\n" + "^")); + + EXPECT_THAT(Eval("array + 1"), IsOk()); + EXPECT_THAT(Eval("1 + array"), IsOk()); + EXPECT_THAT(Eval("array_ref + 1"), IsOk()); + EXPECT_THAT(Eval("1 + array_ref"), IsOk()); + + EXPECT_THAT(Eval("array - 1"), IsOk()); + EXPECT_THAT(Eval("array_ref - 1"), IsOk()); + EXPECT_THAT( + Eval("1 - array"), + IsError("invalid operands to binary expression ('int' and 'int[10]')\n" + "1 - array\n" + " ^")); + + EXPECT_THAT(Eval("array - array"), IsEqual("0")); + EXPECT_THAT(Eval("array - array_ref"), IsEqual("0")); + EXPECT_THAT(Eval("array_ref - array_ref"), IsEqual("0")); + EXPECT_THAT( + Eval("array + array"), + IsError( + "invalid operands to binary expression ('int[10]' and 'int[10]')\n" + "array + array\n" + " ^")); +} + +TEST_F(EvalTest, PointerPointerArithmeticFloat) { + EXPECT_THAT( + Eval("(int*)0 + 1.1"), + IsError("invalid operands to binary expression ('int *' and 'double')")); + EXPECT_THAT( + Eval("1.1 + (int*)0"), + IsError("invalid operands to binary expression ('double' and 'int *')")); + EXPECT_THAT( + Eval("(int*)0 - 1.1"), + IsError("invalid operands to binary expression ('int *' and 'double')")); +} + +TEST_F(EvalTest, PointerPointerComparison) { + EXPECT_THAT(Eval("p_void == p_void"), IsEqual("true")); + EXPECT_THAT(Eval("p_void == p_char1"), IsEqual("true")); + EXPECT_THAT(Eval("p_void != p_char1"), IsEqual("false")); + EXPECT_THAT(Eval("p_void > p_char1"), IsEqual("false")); + EXPECT_THAT(Eval("p_void >= p_char1"), IsEqual("true")); + EXPECT_THAT(Eval("p_void < (p_char1 + 1)"), IsEqual("true")); + EXPECT_THAT(Eval("pp_void0 + 1 == pp_void1"), IsEqual("true")); + + EXPECT_THAT(Eval("(void*)1 == (void*)1"), IsEqual("true")); + EXPECT_THAT(Eval("(void*)1 != (void*)1"), IsEqual("false")); + EXPECT_THAT(Eval("(void*)2 > (void*)1"), IsEqual("true")); + EXPECT_THAT(Eval("(void*)2 < (void*)1"), IsEqual("false")); + + EXPECT_THAT(Eval("(void*)1 == (char*)1"), IsEqual("true")); + EXPECT_THAT(Eval("(char*)1 != (void*)1"), IsEqual("false")); + EXPECT_THAT(Eval("(void*)2 > (char*)1"), IsEqual("true")); + EXPECT_THAT(Eval("(char*)2 < (void*)1"), IsEqual("false")); +} + +TEST_F(EvalTest, PointerIntegerComparison) { + EXPECT_THAT(Eval("(void*)0 == 0"), IsEqual("true")); + EXPECT_THAT(Eval("0 != (void*)0"), IsEqual("false")); + + EXPECT_THAT(Eval("(void*)0 == nullptr"), IsEqual("true")); + EXPECT_THAT(Eval("(void*)0 != nullptr"), IsEqual("false")); + EXPECT_THAT(Eval("nullptr == (void*)1"), IsEqual("false")); + EXPECT_THAT(Eval("nullptr != (void*)1"), IsEqual("true")); + + EXPECT_THAT(Eval("nullptr == nullptr"), IsEqual("true")); + EXPECT_THAT(Eval("nullptr != nullptr"), IsEqual("false")); + + EXPECT_THAT(Eval("nullptr == 0"), IsEqual("true")); + EXPECT_THAT(Eval("0 != nullptr"), IsEqual("false")); + EXPECT_THAT(Eval("nullptr == 0U"), IsEqual("true")); + EXPECT_THAT(Eval("0L != nullptr"), IsEqual("false")); + EXPECT_THAT(Eval("nullptr == 0UL"), IsEqual("true")); + EXPECT_THAT(Eval("0ULL != nullptr"), IsEqual("false")); + EXPECT_THAT(Eval("nullptr == 0x0"), IsEqual("true")); + EXPECT_THAT(Eval("0b0 != nullptr"), IsEqual("false")); + EXPECT_THAT(Eval("nullptr == 00"), IsEqual("true")); + EXPECT_THAT(Eval("0x0LLU != nullptr"), IsEqual("false")); + + EXPECT_THAT(Eval("0 == std_nullptr_t"), IsEqual("true")); + EXPECT_THAT(Eval("std_nullptr_t != 0"), IsEqual("false")); + + EXPECT_THAT(Eval("(void*)0 > nullptr"), + IsError("invalid operands to binary expression ('void *' and " + "'std::nullptr_t')")); + + EXPECT_THAT(Eval("nullptr > 0"), + IsError("invalid operands to binary expression ('std::nullptr_t' " + "and 'int')")); + + EXPECT_THAT(Eval("1 == nullptr"), + IsError("invalid operands to binary expression ('int' and " + "'std::nullptr_t')")); + + EXPECT_THAT(Eval("nullptr == (int)0"), + IsError("invalid operands to binary expression ('std::nullptr_t' " + "and 'int')")); + + EXPECT_THAT(Eval("false == nullptr"), + IsError("invalid operands to binary expression ('bool' and " + "'std::nullptr_t')")); + + EXPECT_THAT(Eval("nullptr == (true ? 0 : 0)"), + IsError("invalid operands to binary expression ('std::nullptr_t' " + "and 'int')")); + + EXPECT_THAT( + Eval("nullptr > nullptr"), + IsError("invalid operands to binary expression ('std::nullptr_t' and " + "'std::nullptr_t')")); + + // These are not allowed by C++, but we support it as an extension. + EXPECT_THAT(Eval("(void*)1 == 1"), IsEqual("true")); + EXPECT_THAT(Eval("(void*)1 == 0"), IsEqual("false")); + EXPECT_THAT(Eval("(void*)1 > 0"), IsEqual("true")); + EXPECT_THAT(Eval("(void*)1 < 0"), IsEqual("false")); + EXPECT_THAT(Eval("1 > (void*)0"), IsEqual("true")); + EXPECT_THAT(Eval("2 < (void*)3"), IsEqual("true")); + + // Integer is converted to uintptr_t, so negative numbers because large + // positive numbers. + EXPECT_THAT(Eval("(void*)0xffffffffffffffff == -1"), IsEqual("true")); + EXPECT_THAT(Eval("(void*)-1 == -1"), IsEqual("true")); + EXPECT_THAT(Eval("(void*)1 > -1"), IsEqual("false")); +} + +TEST_F(EvalTest, TestPointerDereference) { + EXPECT_THAT(Eval("*p_int0"), IsEqual("0")); + EXPECT_THAT(Eval("*p_int0 + 1"), IsEqual("1")); + EXPECT_THAT(Eval("*cp_int5"), IsEqual("5")); + EXPECT_THAT(Eval("*cp_int5 - 1"), IsEqual("4")); + + EXPECT_THAT(Eval("&*p_null"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("&p_null[4]"), + IsEqual(Is32Bit() ? "0x00000010" : "0x0000000000000010")); + EXPECT_THAT(Eval("&*(int*)0"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("&((int*)0)[1]"), + IsEqual(Is32Bit() ? "0x00000004" : "0x0000000000000004")); + + EXPECT_THAT(Eval("&p_void[0]"), + IsError("subscript of pointer to incomplete type 'void'")); + EXPECT_THAT(Eval("&*p_void"), + XFail(IsError("indirection not permitted on operand of type" + " 'void *'"))); + EXPECT_THAT(Eval("&pp_void0[2]"), IsOk()); + + EXPECT_THAT(Eval("**pp_int0"), IsEqual("0")); + EXPECT_THAT(Eval("**pp_int0 + 1"), IsEqual("1")); + EXPECT_THAT(Eval("&**pp_int0"), IsOk()); + EXPECT_THAT(Eval("&**pp_int0 + 1"), IsOk()); + + EXPECT_THAT(Eval("&(true ? *p_null : *p_null)"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + + EXPECT_THAT(Eval("&(false ? *p_null : *p_null)"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("&*(true ? p_null : nullptr)"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); +} + +TEST_F(EvalTest, TestLogicalOperators) { + EXPECT_THAT(Eval("1 > 2"), IsEqual("false")); + EXPECT_THAT(Eval("1 == 1"), IsEqual("true")); + EXPECT_THAT(Eval("1 > 0.1"), IsEqual("true")); + EXPECT_THAT(Eval("1 && 2"), IsEqual("true")); + EXPECT_THAT(Eval("0 && 1"), IsEqual("false")); + EXPECT_THAT(Eval("0 || 1"), IsEqual("true")); + EXPECT_THAT(Eval("0 || 0"), IsEqual("false")); + + EXPECT_THAT(Eval("!1"), IsEqual("false")); + EXPECT_THAT(Eval("!!1"), IsEqual("true")); + + EXPECT_THAT(Eval("!trueVar"), IsEqual("false")); + EXPECT_THAT(Eval("!!trueVar"), IsEqual("true")); + EXPECT_THAT(Eval("!falseVar"), IsEqual("true")); + EXPECT_THAT(Eval("!!falseVar"), IsEqual("false")); + + EXPECT_THAT(Eval("trueVar && true"), IsEqual("true")); + EXPECT_THAT(Eval("trueVar && (2 > 1)"), IsEqual("true")); + EXPECT_THAT(Eval("trueVar && (2 < 1)"), IsEqual("false")); + + EXPECT_THAT(Eval("falseVar || true"), IsEqual("true")); + EXPECT_THAT(Eval("falseVar && true"), IsEqual("false")); + EXPECT_THAT(Eval("falseVar || (2 > 1)"), IsEqual("true")); + EXPECT_THAT(Eval("falseVar || (2 < 1)"), IsEqual("false")); + + EXPECT_THAT(Eval("true || __doesnt_exist"), + IsError("use of undeclared identifier '__doesnt_exist'")); + EXPECT_THAT(Eval("false && __doesnt_exist"), + IsError("use of undeclared identifier '__doesnt_exist'")); + + EXPECT_THAT(Eval("!p_ptr"), IsEqual("false")); + EXPECT_THAT(Eval("!!p_ptr"), IsEqual("true")); + EXPECT_THAT(Eval("p_ptr && true"), IsEqual("true")); + EXPECT_THAT(Eval("p_ptr && false"), IsEqual("false")); + EXPECT_THAT(Eval("!p_nullptr"), IsEqual("true")); + EXPECT_THAT(Eval("!!p_nullptr"), IsEqual("false")); + EXPECT_THAT(Eval("p_nullptr || true"), IsEqual("true")); + EXPECT_THAT(Eval("p_nullptr || false"), IsEqual("false")); + + EXPECT_THAT(Eval("!array"), IsEqual("false")); + EXPECT_THAT(Eval("!!array"), IsEqual("true")); + EXPECT_THAT(Eval("array || true"), IsEqual("true")); + EXPECT_THAT(Eval("false || array"), IsEqual("true")); + EXPECT_THAT(Eval("array && true"), IsEqual("true")); + EXPECT_THAT(Eval("array && false"), IsEqual("false")); + + EXPECT_THAT(Eval("false || !s"), + IsError("invalid argument type 'S' to unary expression\n" + "false || !s\n" + " ^")); + EXPECT_THAT( + Eval("s || false"), + IsError("value of type 'S' is not contextually convertible to 'bool'\n" + "s || false\n" + "^")); + EXPECT_THAT( + Eval("true || s"), + IsError("value of type 'S' is not contextually convertible to 'bool'\n" + "true || s\n" + " ^")); + EXPECT_THAT( + Eval("s ? 1 : 2"), + IsError("value of type 'S' is not contextually convertible to 'bool'")); +} + +TEST_F(EvalTest, TestLocalVariables) { + EXPECT_THAT(Eval("a"), IsEqual("1")); + EXPECT_THAT(Eval("b"), IsEqual("2")); + EXPECT_THAT(Eval("a + b"), IsEqual("3")); + + EXPECT_THAT(Eval("c + 1"), IsEqual("-2")); + EXPECT_THAT(Eval("s + 1"), IsEqual("5")); + EXPECT_THAT(Eval("c + s"), IsEqual("1")); + + EXPECT_THAT(Eval("__test_non_variable + 1"), + IsError("use of undeclared identifier '__test_non_variable'")); +} + +TEST_F(EvalTest, TestMemberOf) { + EXPECT_THAT(Eval("s.x"), IsEqual("1")); + EXPECT_THAT(Eval("s.r"), XFail(IsEqual("2"))); + EXPECT_THAT(Eval("s.r + 1"), IsEqual("3")); + EXPECT_THAT(Eval("sr.x"), IsEqual("1")); + EXPECT_THAT(Eval("sr.r"), XFail(IsEqual("2"))); + EXPECT_THAT(Eval("sr.r + 1"), IsEqual("3")); + EXPECT_THAT(Eval("sp->x"), IsEqual("1")); + EXPECT_THAT(Eval("sp->r"), XFail(IsEqual("2"))); + EXPECT_THAT(Eval("sp->r + 1"), IsEqual("3")); + EXPECT_THAT(Eval("sarr->x"), IsEqual("5")); + EXPECT_THAT(Eval("sarr->r"), XFail(IsEqual("2"))); + EXPECT_THAT(Eval("sarr->r + 1"), IsEqual("3")); + EXPECT_THAT(Eval("(sarr + 1)->x"), IsEqual("1")); + + EXPECT_THAT( + Eval("sp->4"), + IsError( + ":1:5: expected 'identifier', got: <'4' (numeric_constant)>\n" + "sp->4\n" + " ^")); + EXPECT_THAT(Eval("sp->foo"), IsError("no member named 'foo' in 'Sx'")); + EXPECT_THAT( + Eval("sp->r / (void*)0"), + IsError("invalid operands to binary expression ('int' and 'void *')")); + + EXPECT_THAT(Eval("sp.x"), IsError("member reference type 'Sx *' is a " + "pointer; did you mean to use '->'")); + EXPECT_THAT( + Eval("sarr.x"), + IsError( + "member reference base type 'Sx[2]' is not a structure or union")); + + // Test for record typedefs. + EXPECT_THAT(Eval("sa.x"), IsEqual("3")); + EXPECT_THAT(Eval("sa.y"), IsEqual("'\\x04'")); + + // TODO(werat): Implement address-of-member-or combination. + // EXPECT_THAT(Eval("&((Sx*)0)->x"), IsEqual("0x0000000000000000")); + // EXPECT_THAT(Eval("&((Sx*)0)->y"), IsEqual("0x0000000000000010")); + // EXPECT_THAT(Eval("&(*(Sx*)0).x"), IsEqual("0x0000000000000000")); + // EXPECT_THAT(Eval("&(*(Sx*)0).y"), IsEqual("0x0000000000000010")); +} + +TEST_F(EvalTest, TestMemberOfInheritance) { + EXPECT_THAT(Eval("a.a_"), IsEqual("1")); + EXPECT_THAT(Eval("b.b_"), IsEqual("2")); + EXPECT_THAT(Eval("c.a_"), IsEqual("1")); + EXPECT_THAT(Eval("c.b_"), IsEqual("2")); + EXPECT_THAT(Eval("c.c_"), IsEqual("3")); + EXPECT_THAT(Eval("d.a_"), IsEqual("1")); + EXPECT_THAT(Eval("d.b_"), IsEqual("2")); + EXPECT_THAT(Eval("d.c_"), IsEqual("3")); + EXPECT_THAT(Eval("d.d_"), IsEqual("4")); + EXPECT_THAT(Eval("d.fa_.a_"), IsEqual("5")); + + EXPECT_THAT(Eval("bat.weight_"), IsEqual("10")); + + EXPECT_THAT(Eval("plugin.x"), IsEqual("1")); + EXPECT_THAT(Eval("plugin.y"), IsEqual("2")); + + EXPECT_THAT(Eval("engine.x"), IsEqual("1")); + EXPECT_THAT(Eval("engine.y"), IsEqual("2")); + EXPECT_THAT(Eval("engine.z"), IsEqual("3")); + + EXPECT_THAT(Eval("parent_base->x"), IsEqual("1")); + EXPECT_THAT(Eval("parent_base->y"), IsEqual("2")); + EXPECT_THAT(Eval("parent->x"), IsEqual("1")); + EXPECT_THAT(Eval("parent->y"), IsEqual("2")); + EXPECT_THAT(Eval("parent->z"), IsEqual("3")); +} + +TEST_F(EvalTest, TestMemberOfAnonymousMember) { + EXPECT_THAT(Eval("a.x"), IsEqual("1")); + EXPECT_THAT(Eval("a.y"), IsEqual("2")); + + EXPECT_THAT(Eval("b.x"), IsError("no member named 'x' in 'B'")); + EXPECT_THAT(Eval("b.y"), IsError("no member named 'y' in 'B'")); + EXPECT_THAT(Eval("b.z"), IsEqual("3")); + EXPECT_THAT(Eval("b.w"), IsEqual("4")); + EXPECT_THAT(Eval("b.a.x"), IsEqual("1")); + EXPECT_THAT(Eval("b.a.y"), IsEqual("2")); + + EXPECT_THAT(Eval("c.x"), IsEqual("5")); + EXPECT_THAT(Eval("c.y"), IsEqual("6")); + + EXPECT_THAT(Eval("d.x"), IsEqual("7")); + EXPECT_THAT(Eval("d.y"), IsEqual("8")); + EXPECT_THAT(Eval("d.z"), IsEqual("9")); + EXPECT_THAT(Eval("d.w"), IsEqual("10")); + + EXPECT_THAT(Eval("e.x"), IsError("no member named 'x' in 'E'")); + EXPECT_THAT(Eval("f.x"), IsError("no member named 'x' in 'F'")); + EXPECT_THAT(Eval("f.named_field.x"), IsEqual("12")); + + EXPECT_THAT(Eval("unnamed_derived.x"), IsEqual("1")); + EXPECT_THAT(Eval("unnamed_derived.y"), IsEqual("2")); + EXPECT_THAT(Eval("unnamed_derived.z"), IsEqual("13")); + + EXPECT_THAT(Eval("derb.x"), IsError("no member named 'x' in 'DerivedB'")); + EXPECT_THAT(Eval("derb.y"), IsError("no member named 'y' in 'DerivedB'")); + EXPECT_THAT(Eval("derb.z"), IsEqual("3")); + EXPECT_THAT(Eval("derb.w"), IsEqual("14")); + EXPECT_THAT(Eval("derb.k"), IsEqual("15")); + EXPECT_THAT(Eval("derb.a.x"), IsEqual("1")); + EXPECT_THAT(Eval("derb.a.y"), IsEqual("2")); +} + +TEST_F(EvalTest, TestGlobalVariableLookup) { + EXPECT_THAT(Eval("globalVar"), IsEqual("-559038737")); // 0xDEADBEEF + EXPECT_THAT(Eval("globalPtr"), IsOk()); + EXPECT_THAT(Eval("globalRef"), IsEqual("-559038737")); + EXPECT_THAT(Eval("::globalPtr"), IsOk()); + EXPECT_THAT(Eval("::globalRef"), IsEqual("-559038737")); + + EXPECT_THAT(Eval("externGlobalVar"), + XFail(IsEqual("12648430"))); // 0x00C0FFEE + EXPECT_THAT(Eval("::externGlobalVar"), XFail(IsEqual("12648430"))); + + EXPECT_THAT(Eval("ns::globalVar"), IsEqual("13")); + EXPECT_THAT(Eval("ns::globalPtr"), IsOk()); + EXPECT_THAT(Eval("ns::globalRef"), IsEqual("13")); + EXPECT_THAT(Eval("::ns::globalVar"), IsEqual("13")); + EXPECT_THAT(Eval("::ns::globalPtr"), IsOk()); +} + +TEST_F(EvalTest, TestInstanceVariables) { + EXPECT_THAT(Eval("this->field_"), IsEqual("1")); + EXPECT_THAT(Eval("this.field_"), + IsError("member reference type 'TestMethods *' is a pointer; did " + "you mean to use '->'?")); + + EXPECT_THAT(Eval("c.field_"), IsEqual("-1")); + EXPECT_THAT(Eval("c_ref.field_"), IsEqual("-1")); + EXPECT_THAT(Eval("c_ptr->field_"), IsEqual("-1")); + EXPECT_THAT(Eval("c->field_"), IsError("member reference type 'C' is not a " + "pointer; did you mean to use '.'?")); +} + +TEST_F(EvalTest, TestIndirection) { + EXPECT_THAT(Eval("*p"), IsEqual("1")); + EXPECT_THAT(Eval("p"), IsOk()); + EXPECT_THAT(Eval("*my_p"), IsEqual("1")); + EXPECT_THAT(Eval("my_p"), IsOk()); + EXPECT_THAT(Eval("*my_pr"), IsEqual("1")); + EXPECT_THAT(Eval("my_pr"), IsOk()); + + EXPECT_THAT(Eval("*1"), + IsError("indirection requires pointer operand ('int' invalid)")); + EXPECT_THAT(Eval("*val"), + IsError("indirection requires pointer operand ('int' invalid)")); +} + +TEST_F(EvalTest, TestAddressOf) { + EXPECT_THAT(Eval("&x"), IsOk()); + EXPECT_THAT(Eval("r"), IsOk()); + EXPECT_THAT(Eval("&r"), IsOk()); + EXPECT_THAT(Eval("pr"), IsOk()); + EXPECT_THAT(Eval("&pr"), IsOk()); + EXPECT_THAT(Eval("my_pr"), IsOk()); + EXPECT_THAT(Eval("&my_pr"), IsOk()); + + EXPECT_THAT(Eval("&x == &r"), IsEqual("true")); + EXPECT_THAT(Eval("&x != &r"), IsEqual("false")); + + EXPECT_THAT(Eval("&p == &pr"), IsEqual("true")); + EXPECT_THAT(Eval("&p != &pr"), IsEqual("false")); + EXPECT_THAT(Eval("&p == &my_pr"), IsEqual("true")); + EXPECT_THAT(Eval("&p != &my_pr"), IsEqual("false")); + + EXPECT_THAT(Eval("&globalVar"), IsOk()); + EXPECT_THAT(Eval("&externGlobalVar"), XFail(IsOk())); + EXPECT_THAT(Eval("&s_str"), IsOk()); + EXPECT_THAT(Eval("¶m"), IsOk()); + + EXPECT_THAT(Eval("&1"), + IsError("cannot take the address of an rvalue of type 'int'")); + EXPECT_THAT(Eval("&0.1"), + IsError("cannot take the address of an rvalue of type 'double'")); + + EXPECT_THAT( + Eval("&this"), + IsError("cannot take the address of an rvalue of type 'TestMethods *'")); + EXPECT_THAT( + Eval("&(&s_str)"), + IsError("cannot take the address of an rvalue of type 'const char **'")); + + EXPECT_THAT(Eval("&(true ? x : x)"), IsOk()); + EXPECT_THAT(Eval("&(true ? 1 : 1)"), + IsError("cannot take the address of an rvalue of type 'int'")); + + EXPECT_THAT(Eval("&(true ? c : c)"), IsOk()); + EXPECT_THAT(Eval("&(true ? c : (char)1)"), + IsError("cannot take the address of an rvalue of type 'char'")); + EXPECT_THAT(Eval("&(true ? c : 1)"), + IsError("cannot take the address of an rvalue of type 'int'")); +} + +TEST_F(EvalTest, TestSubscript) { + // const char* + EXPECT_THAT(Eval("char_ptr[0]"), IsEqual("'l'")); + EXPECT_THAT(Eval("1[char_ptr]"), IsEqual("'o'")); + + // const char[] + EXPECT_THAT(Eval("char_arr[0]"), IsEqual("'i'")); + EXPECT_THAT(Eval("1[char_arr]"), IsEqual("'p'")); + + // Boolean types are integral too! + EXPECT_THAT(Eval("int_arr[false]"), IsEqual("1")); + EXPECT_THAT(Eval("true[int_arr]"), IsEqual("2")); + + // As well as unscoped enums. + EXPECT_THAT(Eval("int_arr[enum_one]"), IsEqual("2")); + EXPECT_THAT(Eval("enum_one[int_arr]"), IsEqual("2")); + + // But floats are not. + EXPECT_THAT(Eval("int_arr[1.0]"), + IsError("array subscript is not an integer")); + + // Base should be a "pointer to T" and index should be of an integral type. + EXPECT_THAT(Eval("char_arr[char_ptr]"), + IsError("array subscript is not an integer")); + EXPECT_THAT(Eval("1[2]"), + IsError("subscripted value is not an array or pointer")); + + // Test when base and index are references. + EXPECT_THAT(Eval("c_arr[0].field_"), IsEqual("0")); + EXPECT_THAT(Eval("c_arr[idx_1_ref].field_"), IsEqual("1")); + EXPECT_THAT(Eval("c_arr[enum_ref].field_"), IsEqual("1")); + EXPECT_THAT(Eval("c_arr_ref[0].field_"), IsEqual("0")); + EXPECT_THAT(Eval("c_arr_ref[idx_1_ref].field_"), IsEqual("1")); + EXPECT_THAT(Eval("c_arr_ref[enum_ref].field_"), IsEqual("1")); + + // Test when base and index are typedefs. + bool compare_types = true; +#if LLVM_VERSION_MAJOR < 12 + // Older LLVM versions return canonical types when accessing array elements. + compare_types = false; +#endif + EXPECT_THAT(Eval("td_int_arr[0]"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("td_int_arr[td_int_idx_1]"), IsEqual("2", compare_types)); + EXPECT_THAT(Eval("td_int_arr[td_td_int_idx_2]"), IsEqual("3", compare_types)); + EXPECT_THAT(Eval("td_int_ptr[0]"), IsEqual("1")); + EXPECT_THAT(Eval("td_int_ptr[td_int_idx_1]"), IsEqual("2")); + EXPECT_THAT(Eval("td_int_ptr[td_td_int_idx_2]"), IsEqual("3")); + // Both typedefs and refs! + EXPECT_THAT(Eval("td_int_arr_ref[td_int_idx_1_ref]"), + IsEqual("2", compare_types)); + + // Test for index out of bounds. + EXPECT_THAT(Eval("int_arr[42]"), IsOk()); + EXPECT_THAT(Eval("int_arr[100]"), IsOk()); + + // Test for negative index. + EXPECT_THAT(Eval("int_arr[-1]"), IsOk()); + EXPECT_THAT(Eval("int_arr[-42]"), IsOk()); + + // Test for "max unsigned char". + EXPECT_THAT(Eval("uint8_arr[uchar_idx]"), IsEqual("'\\xab'", compare_types)); + + // Test address-of of the subscripted value. + EXPECT_THAT(Eval("(&c_arr[1])->field_"), XFail(IsEqual("1"))); +} + +TEST_F(EvalTest, TestCStyleCastBuiltins) { + EXPECT_THAT(Eval("(int)1"), IsOk()); + EXPECT_THAT(Eval("(long long)1"), IsOk()); + EXPECT_THAT(Eval("(unsigned long)1"), IsOk()); + EXPECT_THAT(Eval("(long const const)1"), IsOk()); + EXPECT_THAT(Eval("(long const long)1"), IsOk()); + + EXPECT_THAT(Eval("(char*)1"), IsOk()); + EXPECT_THAT(Eval("(long long**)1"), IsOk()); + EXPECT_THAT(Eval("(const long const long const* const const)1"), IsOk()); + + EXPECT_THAT( + Eval("(long&*)1"), + IsError( + "'type name' declared as a pointer to a reference of type 'long &'\n" + "(long&*)1\n" + " ^")); + + EXPECT_THAT(Eval("(long& &)1"), + IsError("type name declared as a reference to a reference\n" + "(long& &)1\n" + " ^")); + + EXPECT_THAT( + Eval("(long 1)1"), + IsError(":1:7: expected 'r_paren', got: <'1' (numeric_constant)>\n" + "(long 1)1\n" + " ^")); +} + +TEST_F(EvalTest, TestCStyleCastBasicType) { + // Test with integer literals. + EXPECT_THAT(Eval("(char)1"), IsEqual("'\\x01'")); + EXPECT_THAT(Eval("(unsigned char)-1"), IsEqual("'\\xff'")); + EXPECT_THAT(Eval("(short)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(unsigned short)-1"), IsEqual("65535")); + EXPECT_THAT(Eval("(long long)1"), IsEqual("1")); + EXPECT_THAT(Eval("(unsigned long long)-1"), IsEqual("18446744073709551615")); + EXPECT_THAT(Eval("(short)65534"), IsEqual("-2")); + EXPECT_THAT(Eval("(unsigned short)100000"), IsEqual("34464")); + EXPECT_THAT(Eval("(int)false"), IsEqual("0")); + EXPECT_THAT(Eval("(int)true"), IsEqual("1")); + EXPECT_THAT(Eval("(float)1"), IsEqual("1")); + EXPECT_THAT(Eval("(float)1.1"), IsEqual("1.10000002")); + EXPECT_THAT(Eval("(float)1.1f"), IsEqual("1.10000002")); + EXPECT_THAT(Eval("(float)-1.1"), IsEqual("-1.10000002")); + EXPECT_THAT(Eval("(float)-1.1f"), IsEqual("-1.10000002")); + EXPECT_THAT(Eval("(float)false"), IsEqual("0")); + EXPECT_THAT(Eval("(float)true"), IsEqual("1")); + EXPECT_THAT(Eval("(double)1"), IsEqual("1")); + EXPECT_THAT(Eval("(double)1.1"), IsEqual("1.1000000000000001")); + EXPECT_THAT(Eval("(double)1.1f"), IsEqual("1.1000000238418579")); + EXPECT_THAT(Eval("(double)-1.1"), IsEqual("-1.1000000000000001")); + EXPECT_THAT(Eval("(double)-1.1f"), IsEqual("-1.1000000238418579")); + EXPECT_THAT(Eval("(double)false"), IsEqual("0")); + EXPECT_THAT(Eval("(double)true"), IsEqual("1")); + EXPECT_THAT(Eval("(int)1.1"), IsEqual("1")); + EXPECT_THAT(Eval("(int)1.1f"), IsEqual("1")); + EXPECT_THAT(Eval("(int)-1.1"), IsEqual("-1")); + EXPECT_THAT(Eval("(long)1.1"), IsEqual("1")); + EXPECT_THAT(Eval("(long)-1.1f"), IsEqual("-1")); + EXPECT_THAT(Eval("(bool)0"), IsEqual("false")); + EXPECT_THAT(Eval("(bool)0.0"), IsEqual("false")); + EXPECT_THAT(Eval("(bool)0.0f"), IsEqual("false")); + EXPECT_THAT(Eval("(bool)3"), IsEqual("true")); + EXPECT_THAT(Eval("(bool)-3"), IsEqual("true")); + EXPECT_THAT(Eval("(bool)-3.4"), IsEqual("true")); + EXPECT_THAT(Eval("(bool)-0.1"), IsEqual("true")); + EXPECT_THAT(Eval("(bool)-0.1f"), IsEqual("true")); + + EXPECT_THAT(Eval("&(int)1"), + IsError("cannot take the address of an rvalue of type 'int'")); + + // Test with variables. + EXPECT_THAT(Eval("(char)a"), IsEqual("'\\x01'")); + EXPECT_THAT(Eval("(unsigned char)na"), IsEqual("'\\xff'")); + EXPECT_THAT(Eval("(short)na"), IsEqual("-1")); + EXPECT_THAT(Eval("(unsigned short)-a"), IsEqual("65535")); + EXPECT_THAT(Eval("(long long)a"), IsEqual("1")); + EXPECT_THAT(Eval("(unsigned long long)-1"), IsEqual("18446744073709551615")); + EXPECT_THAT(Eval("(float)a"), IsEqual("1")); + EXPECT_THAT(Eval("(float)f"), IsEqual("1.10000002")); + EXPECT_THAT(Eval("(double)f"), IsEqual("1.1000000238418579")); + EXPECT_THAT(Eval("(int)f"), IsEqual("1")); + EXPECT_THAT(Eval("(long)f"), IsEqual("1")); + EXPECT_THAT(Eval("(bool)finf"), IsEqual("true")); + EXPECT_THAT(Eval("(bool)fnan"), IsEqual("true")); + EXPECT_THAT(Eval("(bool)fsnan"), IsEqual("true")); + EXPECT_THAT(Eval("(bool)fmax"), IsEqual("true")); + EXPECT_THAT(Eval("(bool)fdenorm"), IsEqual("true")); + + EXPECT_THAT( + Eval("(int)ns_foo_"), + IsError( + "cannot convert 'ns::Foo' to 'int' without a conversion operator")); + + // Test with typedefs and namespaces. + EXPECT_THAT(Eval("(myint)1"), IsEqual("1")); + EXPECT_THAT(Eval("(myint)1LL"), IsEqual("1")); + EXPECT_THAT(Eval("(ns::myint)1"), IsEqual("1")); + EXPECT_THAT(Eval("(::ns::myint)1"), IsEqual("1")); + EXPECT_THAT(Eval("(::ns::myint)myint_"), IsEqual("1")); + + EXPECT_THAT(Eval("(int)myint_"), IsEqual("1")); + EXPECT_THAT(Eval("(int)ns_myint_"), IsEqual("2")); + EXPECT_THAT(Eval("(long long)myint_"), IsEqual("1")); + EXPECT_THAT(Eval("(long long)ns_myint_"), IsEqual("2")); + EXPECT_THAT(Eval("(::ns::myint)myint_"), IsEqual("1")); + + EXPECT_THAT(Eval("(ns::inner::mydouble)1"), IsEqual("1")); + EXPECT_THAT(Eval("(::ns::inner::mydouble)1.2"), IsEqual("1.2")); + EXPECT_THAT(Eval("(ns::inner::mydouble)myint_"), IsEqual("1")); + EXPECT_THAT(Eval("(::ns::inner::mydouble)ns_inner_mydouble_"), + IsEqual("1.2")); + EXPECT_THAT(Eval("(myint)ns_inner_mydouble_"), IsEqual("1")); + + // Test with pointers and arrays. + EXPECT_THAT(Eval("(long long)ap"), IsOk()); + EXPECT_THAT(Eval("(unsigned long long)vp"), IsOk()); + EXPECT_THAT(Eval("(long long)arr"), IsOk()); + EXPECT_THAT(Eval("(bool)ap"), IsEqual("true")); + EXPECT_THAT(Eval("(bool)(int*)0x00000000"), IsEqual("false")); + EXPECT_THAT(Eval("(bool)nullptr"), IsEqual("false")); + EXPECT_THAT(Eval("(bool)arr"), IsEqual("true")); + EXPECT_THAT( + Eval("(char)ap"), + IsError("cast from pointer to smaller type 'char' loses information")); + if (Is32Bit()) { + EXPECT_THAT(Eval("(int)arr"), IsOk()); + } else { + EXPECT_THAT( + Eval("(int)arr"), + IsError("cast from pointer to smaller type 'int' loses information")); + } + +#ifdef _WIN32 + EXPECT_THAT( + Eval("(long)ap"), + IsError("cast from pointer to smaller type 'long' loses information")); +#endif + + EXPECT_THAT(Eval("(float)ap"), + IsError("C-style cast from 'int *' to 'float' is not allowed")); + EXPECT_THAT(Eval("(float)arr"), + IsError("C-style cast from 'int *' to 'float' is not allowed")); +} + +TEST_F(EvalTest, TestCStyleCastPointer) { + EXPECT_THAT(Eval("(void*)&a"), IsOk()); + EXPECT_THAT(Eval("(void*)ap"), IsOk()); + EXPECT_THAT(Eval("(long long*)vp"), IsOk()); + EXPECT_THAT(Eval("(short int*)vp"), IsOk()); + EXPECT_THAT(Eval("(unsigned long long*)vp"), IsOk()); + EXPECT_THAT(Eval("(unsigned short int*)vp"), IsOk()); + + EXPECT_THAT(Eval("(void*)0"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("(void*)1"), + IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); + EXPECT_THAT(Eval("(void*)a"), + IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); + EXPECT_THAT(Eval("(void*)na"), + IsEqual(Is32Bit() ? "0xffffffff" : "0xffffffffffffffff")); + EXPECT_THAT(Eval("(int*&)ap"), IsOk()); + + EXPECT_THAT( + Eval("(char*) 1.0"), + IsError("cannot cast from type 'double' to pointer type 'char *'")); + + EXPECT_THAT(Eval("*(const int* const)ap"), IsEqual("1")); + EXPECT_THAT(Eval("*(volatile int* const)ap"), IsEqual("1")); + EXPECT_THAT(Eval("*(const int* const)vp"), IsEqual("1")); + EXPECT_THAT(Eval("*(const int* const volatile const)vp"), IsEqual("1")); + EXPECT_THAT(Eval("*(int*)(void*)ap"), IsEqual("1")); + EXPECT_THAT(Eval("*(int*)(const void* const volatile)ap"), IsEqual("1")); + + EXPECT_THAT(Eval("(ns::Foo*)ns_inner_foo_ptr_"), IsOk()); + EXPECT_THAT(Eval("(ns::inner::Foo*)ns_foo_ptr_"), IsOk()); + + EXPECT_THAT(Eval("(int& &)ap"), + IsError("type name declared as a reference to a reference")); + EXPECT_THAT(Eval("(int&*)ap"), IsError("'type name' declared as a pointer " + "to a reference of type 'int &'")); + + GTEST_SKIP() << "Segfault when retrieving result value in the matcher"; + EXPECT_THAT(Eval("(nullptr_t)nullptr"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("(nullptr_t)0"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + + EXPECT_THAT(Eval("(nullptr_t)1"), + IsError("C-style cast from 'int' to 'nullptr_t' (canonically " + "referred to as 'std::nullptr_t')" + " is not allowed")); + EXPECT_THAT(Eval("(nullptr_t)ap"), + IsError("C-style cast from 'int *' to 'nullptr_t' (canonically " + "referred to as 'std::nullptr_t')" + " is not allowed")); +} + +TEST_F(EvalTest, TestCStyleCastNullptrType) { + if (Is32Bit()) { + EXPECT_THAT(Eval("(int)nullptr"), IsOk()); + } else { + EXPECT_THAT( + Eval("(int)nullptr"), + IsError("cast from pointer to smaller type 'int' loses information")); + } + EXPECT_THAT(Eval("(uint64_t)nullptr"), IsEqual("0")); + + EXPECT_THAT(Eval("(void*)nullptr"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("(char*)nullptr"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); +} + +TEST_F(EvalTest, TestCStyleCastArray) { + EXPECT_THAT(Eval("(int*)arr_1d"), IsOk()); + EXPECT_THAT(Eval("(char*)arr_1d"), IsOk()); + EXPECT_THAT(Eval("((char*)arr_1d)[0]"), IsEqual("'\\x01'")); + EXPECT_THAT(Eval("((char*)arr_1d)[1]"), IsEqual("'\\0'")); + + // 2D arrays. + EXPECT_THAT(Eval("(int*)arr_2d"), IsOk()); + EXPECT_THAT(Eval("((int*)arr_2d)[1]"), IsEqual("2")); + EXPECT_THAT(Eval("((int*)arr_2d)[2]"), IsEqual("3")); + EXPECT_THAT(Eval("((int*)arr_2d[1])[1]"), IsEqual("5")); +} + +TEST_F(EvalTest, TestArrayDereference) { + EXPECT_THAT(Eval("*arr_1d"), IsEqual("1")); + EXPECT_THAT(Eval("&*arr_1d"), IsOk()); + EXPECT_THAT(Eval("*(arr_1d + 1)"), IsEqual("2")); + + EXPECT_THAT(Eval("(int*)*arr_2d"), IsOk()); + EXPECT_THAT(Eval("&*arr_2d"), IsOk()); + EXPECT_THAT(Eval("**arr_2d"), IsEqual("1")); + EXPECT_THAT(Eval("*arr_2d[1]"), IsEqual("4")); + EXPECT_THAT(Eval("(*arr_2d)[1]"), IsEqual("2")); + EXPECT_THAT(Eval("**(arr_2d + 1)"), IsEqual("4")); + EXPECT_THAT(Eval("*(*(arr_2d + 1) + 1)"), IsEqual("5")); +} + +TEST_F(EvalTest, TestCStyleCastReference) { + EXPECT_THAT(Eval("((InnerFoo&)arr_1d[1]).a"), IsEqual("2")); + EXPECT_THAT(Eval("((InnerFoo&)arr_1d[1]).b"), IsEqual("3")); + + EXPECT_THAT(Eval("(int&)arr_1d[0]"), IsEqual("1")); + EXPECT_THAT(Eval("(int&)arr_1d[1]"), IsEqual("2")); + + EXPECT_THAT(Eval("(int&)0"), + IsError("C-style cast from rvalue to reference type 'int &'")); + EXPECT_THAT(Eval("&(int&)arr_1d"), IsOk()); +} + +TEST_F(EvalTest, TestCxxStaticCast) { + // Cast to scalars. + EXPECT_THAT(Eval("static_cast(1.1)"), IsEqual("1")); + EXPECT_THAT(Eval("static_cast(1)"), IsEqual("1")); + EXPECT_THAT(Eval("static_cast(128)"), IsEqual("'\\x80'")); + EXPECT_THAT(Eval("static_cast(nullptr)"), IsEqual("false")); + EXPECT_THAT(Eval("static_cast((int*)0)"), IsEqual("false")); + EXPECT_THAT(Eval("static_cast(arr)"), IsEqual("true")); + EXPECT_THAT(Eval("static_cast(u_enum)"), IsEqual("2")); + EXPECT_THAT(Eval("static_cast(s_enum)"), IsEqual("1")); + EXPECT_THAT(Eval("static_cast(5.3)"), IsEqual("5")); + EXPECT_THAT(Eval("static_cast(4)"), IsEqual("4")); + EXPECT_THAT(Eval("static_cast(td_int)"), IsEqual("13")); + + EXPECT_THAT(Eval("static_cast(nullptr)"), + IsError("static_cast from 'std::nullptr_t' to 'long long' is not " + "allowed")); + + EXPECT_THAT( + Eval("static_cast(ptr)"), + IsError("static_cast from 'int *' to 'long long' is not allowed")); + EXPECT_THAT( + Eval("static_cast(arr)"), + IsError("static_cast from 'int *' to 'long long' is not allowed")); + EXPECT_THAT( + Eval("static_cast(parent)"), + IsError( + "cannot convert 'CxxParent' to 'int' without a conversion operator")); + EXPECT_THAT( + Eval("static_cast(base)"), + IsError("static_cast from 'CxxBase *' to 'long long' is not allowed")); + + // Cast to enums. + EXPECT_THAT(Eval("static_cast(0)"), IsEqual("kUZero")); + EXPECT_THAT(Eval("static_cast(s_enum)"), IsEqual("kUOne")); + EXPECT_THAT(Eval("static_cast(2.1)"), IsEqual("kUTwo")); + EXPECT_THAT(Eval("static_cast(true)"), IsEqual("kSOne")); + EXPECT_THAT(Eval("static_cast(0.4f)"), IsEqual("kSZero")); + EXPECT_THAT(Eval("static_cast(td_senum)"), IsEqual("kSOne")); + EXPECT_THAT(Eval("static_cast(UEnum::kUOne)"), IsEqual("kSOne")); + + EXPECT_THAT( + Eval("static_cast(nullptr)"), + IsError("static_cast from 'std::nullptr_t' to 'UEnum' is not allowed")); + + EXPECT_THAT(Eval("static_cast(ptr)"), + IsError("static_cast from 'int *' to 'UEnum' is not allowed")); + EXPECT_THAT( + Eval("static_cast(parent)"), + IsError("static_cast from 'CxxParent' to 'SEnum' is not allowed")); + + // Cast to pointers. + EXPECT_THAT(Eval("static_cast(0)"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("static_cast(nullptr)"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("*static_cast(arr)"), IsEqual("1")); + EXPECT_THAT(Eval("*static_cast(arr)"), IsEqual("1")); + EXPECT_THAT(Eval("static_cast(ptr)"), IsOk()); + EXPECT_THAT(Eval("static_cast((void*)4)"), + IsEqual(Is32Bit() ? "0x00000004" : "0x0000000000000004")); + EXPECT_THAT(Eval("static_cast(ptr)"), + IsError("static_cast from 'int *' to 'long *' is not allowed")); + EXPECT_THAT(Eval("static_cast(arr)"), + IsError("static_cast from 'int *' to 'float *' is not allowed")); + + // Cast to nullptr. + EXPECT_THAT(Eval("static_cast((int)0)"), + IsError("static_cast from 'int' to 'nullptr_t' (canonically " + "referred to as 'std::nullptr_t')" + " is not allowed")); + EXPECT_THAT(Eval("static_cast((void*)0)"), + IsError("static_cast from 'void *' to 'nullptr_t' (canonically " + "referred to as 'std::nullptr_t')" + " is not allowed")); + + // Cast to references. + EXPECT_THAT(Eval("static_cast(parent.b)"), IsEqual("2")); + EXPECT_THAT(Eval("&static_cast(parent.b)"), IsOk()); + EXPECT_THAT( + Eval("static_cast(parent.c)"), + IsError( + "static_cast from 'long long' to 'int &' is not implemented yet")); + EXPECT_THAT(Eval("static_cast(5)"), + IsError("static_cast from rvalue of type 'int' to reference type " + "'int &' is not implemented yet")); + + // Invalid expressions. + EXPECT_THAT(Eval("static_cast<1>(1)"), + IsError("type name requires a specifier or qualifier")); + EXPECT_THAT(Eval("static_cast<>(1)"), + IsError("type name requires a specifier or qualifier")); + EXPECT_THAT(Eval("static_cast(1)"), + IsError("unknown type name 'parent'")); + EXPECT_THAT(Eval("static_cast CxxParent>(1)"), + IsError("two or more data types in declaration of 'type name'")); + + // Cast to nullptr. + GTEST_SKIP() << "Segfault when retrieving result value in the matcher"; + EXPECT_THAT(Eval("static_cast(nullptr)"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("static_cast(0)"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); +} + +TEST_F(EvalTest, TestCastDerivedToBase) { + EXPECT_THAT(Eval("static_cast(&a)->a"), IsEqual("1")); + EXPECT_THAT(Eval("static_cast(&c)->a"), IsEqual("3")); + EXPECT_THAT(Eval("static_cast(&c)->b"), IsEqual("4")); + EXPECT_THAT(Eval("static_cast(&c)->c"), + IsError("no member named 'c' in 'CxxB'")); + EXPECT_THAT(Eval("static_cast(&e)->b"), IsEqual("8")); + EXPECT_THAT(Eval("static_cast(&e)->a"), IsEqual("7")); + EXPECT_THAT(Eval("static_cast(&e)->b"), IsEqual("8")); + EXPECT_THAT(Eval("static_cast(&e)->c"), IsEqual("9")); + EXPECT_THAT(Eval("static_cast(&d)"), + IsError("static_cast from 'CxxD *' to 'CxxB *', which are not " + "related by inheritance, is not allowed")); + + // Cast via virtual inheritance. + EXPECT_THAT(Eval("static_cast(&vc)->a"), IsEqual("12")); + EXPECT_THAT(Eval("static_cast(&vc)->b"), IsEqual("13")); + EXPECT_THAT(Eval("static_cast(&vc)->c"), + IsError("no member named 'c' in 'CxxB'")); + EXPECT_THAT(Eval("static_cast(&ve)->b"), IsEqual("16")); + EXPECT_THAT(Eval("static_cast(&ve)"), + IsError("static_cast from 'CxxVE *' to 'CxxC *', which are not " + "related by inheritance, is not allowed")); + + // Same with references. + EXPECT_THAT(Eval("static_cast(a).a"), IsEqual("1")); + EXPECT_THAT(Eval("static_cast(c).a"), IsEqual("3")); + EXPECT_THAT(Eval("static_cast(c).b"), IsEqual("4")); + EXPECT_THAT(Eval("static_cast(c).c"), + IsError("no member named 'c' in 'CxxB'")); + EXPECT_THAT(Eval("static_cast(e).b"), IsEqual("8")); + EXPECT_THAT(Eval("static_cast(e).a"), IsEqual("7")); + EXPECT_THAT(Eval("static_cast(e).b"), IsEqual("8")); + EXPECT_THAT(Eval("static_cast(e).c"), IsEqual("9")); + EXPECT_THAT(Eval("static_cast(d)"), + IsError("static_cast from 'CxxD' to 'CxxB &', which are not " + "related by inheritance, is not allowed")); + + EXPECT_THAT(Eval("static_cast(vc).a"), IsEqual("12")); + EXPECT_THAT(Eval("static_cast(vc).b"), IsEqual("13")); + EXPECT_THAT(Eval("static_cast(vc).c"), + IsError("no member named 'c' in 'CxxB'")); + EXPECT_THAT(Eval("static_cast(ve).b"), IsEqual("16")); + EXPECT_THAT(Eval("static_cast(ve)"), + IsError("static_cast from 'CxxVE' to 'CxxC &', which are not " + "related by inheritance, is not allowed")); +} + +TEST_F(EvalTest, TestCastBaseToDerived) { + EXPECT_THAT(Eval("static_cast(e_as_b)->a"), IsEqual("7")); + EXPECT_THAT(Eval("static_cast(e_as_b)->b"), IsEqual("8")); + EXPECT_THAT(Eval("static_cast(e_as_b)->c"), IsEqual("9")); + EXPECT_THAT(Eval("static_cast(e_as_b)->d"), IsEqual("10")); + EXPECT_THAT(Eval("static_cast(e_as_b)->e"), IsEqual("11")); + + // Same with references. + EXPECT_THAT(Eval("static_cast(*e_as_b).a"), IsEqual("7")); + EXPECT_THAT(Eval("static_cast(*e_as_b).b"), IsEqual("8")); + EXPECT_THAT(Eval("static_cast(*e_as_b).c"), IsEqual("9")); + EXPECT_THAT(Eval("static_cast(*e_as_b).d"), IsEqual("10")); + EXPECT_THAT(Eval("static_cast(*e_as_b).e"), IsEqual("11")); + + // Base-to-derived conversion isn't possible for virtually inhertied types. + EXPECT_THAT( + Eval("static_cast(ve_as_b)"), + IsError("cannot cast 'CxxB *' to 'CxxVE *' via virtual base 'CxxB'")); + EXPECT_THAT( + Eval("static_cast(*ve_as_b)"), + IsError("cannot cast 'CxxB' to 'CxxVE &' via virtual base 'CxxB'")); +} + +TEST_F(EvalTest, TestCxxDynamicCast) { + // LLDB doesn't support `dynamic_cast` in the expression evaluator. + this->compare_with_lldb_ = false; + + EXPECT_THAT(Eval("dynamic_cast(0)"), + IsError("invalid target type 'int' for dynamic_cast")); + EXPECT_THAT(Eval("dynamic_cast(0)"), + IsError("'int' is not a class type")); + EXPECT_THAT( + Eval("dynamic_cast(1.1)"), + IsError( + "cannot use dynamic_cast to convert from 'double' to 'CxxBase *'")); + EXPECT_THAT(Eval("dynamic_cast((int*)0)"), + IsError("'int' is not a class type")); + EXPECT_THAT(Eval("dynamic_cast(base)"), + IsError("'CxxBase' is not polymorphic")); + EXPECT_THAT(Eval("dynamic_cast(v_base)"), + IsError("dynamic_cast is not supported in this context")); +} + +TEST_F(EvalTest, TestCxxReinterpretCast) { + // Integers and enums can be converted to its own type. + EXPECT_THAT(Eval("reinterpret_cast(true)"), IsEqual("true")); + EXPECT_THAT(Eval("reinterpret_cast(5)"), IsEqual("5")); + EXPECT_THAT(Eval("reinterpret_cast(6)"), IsEqual("6")); + EXPECT_THAT(Eval("reinterpret_cast(td_int)"), IsEqual("13")); + EXPECT_THAT(Eval("reinterpret_cast(100LL)"), IsEqual("100")); + EXPECT_THAT(Eval("reinterpret_cast(u_enum)"), IsEqual("kUTwo")); + EXPECT_THAT(Eval("reinterpret_cast(s_enum)"), IsEqual("kSOne")); + EXPECT_THAT(Eval("reinterpret_cast(s_enum)"), IsEqual("kSOne")); + // Other scalar/enum to scalar/enum casts aren't allowed. + EXPECT_THAT( + Eval("reinterpret_cast(5U)"), + IsError("reinterpret_cast from 'unsigned int' to 'int' is not allowed")); + EXPECT_THAT(Eval("reinterpret_cast(3.14f)"), + IsError("reinterpret_cast from 'float' to 'int' is not allowed")); + EXPECT_THAT( + Eval("reinterpret_cast(2.71)"), + IsError("reinterpret_cast from 'double' to 'double' is not allowed")); + EXPECT_THAT(Eval("reinterpret_cast(s_enum)"), + IsError("reinterpret_cast from 'SEnum' to 'int' is not allowed")); + EXPECT_THAT(Eval("reinterpret_cast(0)"), + IsError("reinterpret_cast from 'int' to 'UEnum' is not allowed")); + EXPECT_THAT( + Eval("reinterpret_cast(s_enum)"), + IsError("reinterpret_cast from 'SEnum' to 'UEnum' is not allowed")); + + // Pointers should be convertible to large enough integral types. + EXPECT_THAT(Eval("reinterpret_cast(ptr)"), IsOk()); + EXPECT_THAT(Eval("reinterpret_cast(arr)"), IsOk()); + EXPECT_THAT(Eval("reinterpret_cast(nullptr)"), IsEqual("0")); + if (Is32Bit()) { + EXPECT_THAT(Eval("reinterpret_cast(ptr)"), IsOk()); + EXPECT_THAT(Eval("reinterpret_cast(ptr)"), IsOk()); + } else { + EXPECT_THAT( + Eval("reinterpret_cast(ptr)"), + IsError("cast from pointer to smaller type 'int' loses information")); + EXPECT_THAT(Eval("reinterpret_cast(ptr)"), + IsError("cast from pointer to smaller type 'td_int_t' (" + "canonically referred to as 'int') loses information")); + } + EXPECT_THAT( + Eval("reinterpret_cast(arr)"), + IsError("cast from pointer to smaller type 'bool' loses information")); + EXPECT_THAT( + Eval("reinterpret_cast(nullptr)"), + IsError("cast from pointer to smaller type 'bool' loses information")); +#ifdef _WIN32 + EXPECT_THAT( + Eval("reinterpret_cast(ptr)"), + IsError("cast from pointer to smaller type 'long' loses information")); +#else + EXPECT_THAT(Eval("reinterpret_cast(ptr)"), IsOk()); +#endif + + // Integers, enums and pointers can be converted to pointers. + EXPECT_THAT(Eval("reinterpret_cast(true)"), + IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); + EXPECT_THAT(Eval("reinterpret_cast(6)"), + IsEqual(Is32Bit() ? "0x00000006" : "0x0000000000000006")); + EXPECT_THAT(Eval("reinterpret_cast(s_enum)"), + IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); + EXPECT_THAT(Eval("reinterpret_cast(u_enum)"), + IsEqual(Is32Bit() ? "0x00000002" : "0x0000000000000002")); + EXPECT_THAT(Eval("reinterpret_cast(ptr)"), IsOk()); + EXPECT_THAT(Eval("*reinterpret_cast(ptr)"), + IsEqual(Is32Bit() ? "0x00000001" : "0x0000000200000001")); + EXPECT_THAT(Eval("*reinterpret_cast(arr)"), IsEqual("1")); + EXPECT_THAT(Eval("*reinterpret_cast(arr)"), + IsEqual("8589934593")); // 8589934593 == 0x0000000200000001 + + // L-values can be converted to reference type. + EXPECT_THAT(Eval("reinterpret_cast(arr[0]).a"), IsEqual("1")); + EXPECT_THAT(Eval("reinterpret_cast(arr).b"), IsEqual("2")); + EXPECT_THAT(Eval("reinterpret_cast(arr[0]).c"), + IsEqual("17179869187")); // 17179869187 == 0x0000000400000003 + EXPECT_THAT(Eval("reinterpret_cast(arr).d"), IsEqual("5")); + EXPECT_THAT(Eval("reinterpret_cast(parent)"), IsOk()); + EXPECT_THAT(Eval("reinterpret_cast(ptr)"), IsOk()); + EXPECT_THAT( + Eval("reinterpret_cast(5)"), + IsError("reinterpret_cast from rvalue to reference type 'int &'")); + + // Is result L-value or R-value? + EXPECT_THAT(Eval("&reinterpret_cast(arr[0])"), IsOk()); + EXPECT_THAT(Eval("&reinterpret_cast(arr[0])"), + IsError("cannot take the address of an rvalue of type 'int'")); + EXPECT_THAT(Eval("&reinterpret_cast(u_enum)"), + IsError("cannot take the address of an rvalue of type 'UEnum'")); + EXPECT_THAT(Eval("&reinterpret_cast(arr)"), + IsError("cannot take the address of an rvalue of type 'int *'")); + + // Casting to nullptr_t or nullptr_t to pointer types isn't allowed. + EXPECT_THAT( + Eval("reinterpret_cast(nullptr)"), + IsError("reinterpret_cast from 'std::nullptr_t' to 'void *' is not " + "allowed")); + GTEST_SKIP() << "Unknown error string mismatch"; + EXPECT_THAT( + Eval("reinterpret_cast(ptr)"), + IsError("reinterpret_cast from 'int *' to 'nullptr_t' " + "(canonically referred to as 'std::nullptr_t') is not allowed")); + EXPECT_THAT( + Eval("reinterpret_cast(0)"), + IsError("reinterpret_cast from 'int' to 'nullptr_t' " + "(canonically referred to as 'std::nullptr_t') is not allowed")); + EXPECT_THAT( + Eval("reinterpret_cast(nullptr)"), + IsError("reinterpret_cast from 'std::nullptr_t' to 'nullptr_t' " + "(canonically referred to as 'std::nullptr_t') is not allowed")); +} + +TEST_F(EvalTest, TestQualifiedId) { + EXPECT_THAT(Eval("::ns::i"), IsEqual("1")); + EXPECT_THAT(Eval("ns::i"), IsEqual("1")); + EXPECT_THAT(Eval("::ns::ns::i"), IsEqual("2")); + EXPECT_THAT(Eval("ns::ns::i"), IsEqual("2")); +} + +TEST_F(EvalTest, TestStaticConstDeclaredInline) { + this->compare_with_lldb_ = false; + + EXPECT_THAT(Eval("::outer::inner::Vars::inline_static"), IsEqual("1.5")); + EXPECT_THAT(Eval("::outer::inner::Vars::static_constexpr"), IsEqual("2")); + EXPECT_THAT(Eval("outer::inner::Vars::inline_static"), IsEqual("1.5")); + EXPECT_THAT(Eval("outer::inner::Vars::static_constexpr"), IsEqual("2")); + + EXPECT_THAT(Eval("::outer::Vars::inline_static"), IsEqual("4.5")); + EXPECT_THAT(Eval("::outer::Vars::static_constexpr"), IsEqual("5")); + EXPECT_THAT(Eval("outer::Vars::inline_static"), IsEqual("4.5")); + EXPECT_THAT(Eval("outer::Vars::static_constexpr"), IsEqual("5")); + + EXPECT_THAT(Eval("::Vars::inline_static"), XFail(IsEqual("7.5"))); + EXPECT_THAT(Eval("::Vars::static_constexpr"), XFail(IsEqual("8"))); + EXPECT_THAT(Eval("Vars::inline_static"), XFail(IsEqual("7.5"))); + EXPECT_THAT(Eval("Vars::static_constexpr"), XFail(IsEqual("8"))); +} + +#ifndef __EMSCRIPTEN__ +TEST_F(EvalTest, DISABLED_TestStaticConstDeclaredInlineScoped) { + EXPECT_THAT(Scope("outer_inner_vars").Eval("inline_static"), IsEqual("1.5")); + EXPECT_THAT(Scope("outer_inner_vars").Eval("static_constexpr"), IsEqual("2")); + EXPECT_THAT(Scope("outer_vars").Eval("inline_static"), IsEqual("4.5")); + EXPECT_THAT(Scope("outer_vars").Eval("static_constexpr"), IsEqual("5")); + EXPECT_THAT(Scope("vars").Eval("inline_static"), IsEqual("7.5")); + EXPECT_THAT(Scope("vars").Eval("inline_static"), IsEqual("8")); +} +#endif + +TEST_F(EvalTest, TestStaticConstDeclaredOutsideTheClass) { +#if LLVM_VERSION_MAJOR < 12 + // Upstream LLDB doesn't handle static const variables. + this->compare_with_lldb_ = false; +#endif + + EXPECT_THAT(Eval("::outer::inner::Vars::static_const"), IsEqual("3")); + EXPECT_THAT(Eval("outer::inner::Vars::static_const"), IsEqual("3")); + EXPECT_THAT(Eval("::outer::Vars::static_const"), IsEqual("6")); + EXPECT_THAT(Eval("outer::Vars::static_const"), IsEqual("6")); + EXPECT_THAT(Eval("::Vars::static_const"), XFail(IsEqual("9"))); + EXPECT_THAT(Eval("Vars::static_const"), XFail(IsEqual("9"))); + + EXPECT_THAT(Eval("::outer::inner::Vars::Nested::static_const"), + IsEqual("10")); + EXPECT_THAT(Eval("outer::inner::Vars::Nested::static_const"), IsEqual("10")); + EXPECT_THAT(Eval("::outer::Vars::Nested::static_const"), IsEqual("20")); + EXPECT_THAT(Eval("outer::Vars::Nested::static_const"), IsEqual("20")); + EXPECT_THAT(Eval("::Vars::Nested::static_const"), XFail(IsEqual("30"))); + EXPECT_THAT(Eval("Vars::Nested::static_const"), XFail(IsEqual("30"))); +} + +#ifndef __EMSCRIPTEN__ +TEST_F(EvalTest, DISABLED_TestStaticConstDeclaredOutsideTheClassScoped) { + EXPECT_THAT(Scope("outer_inner_vars").Eval("static_const"), IsEqual("3")); + EXPECT_THAT(Scope("outer_vars").Eval("static_const"), IsEqual("6")); + EXPECT_THAT(Scope("vars").Eval("static_const"), IsEqual("9")); + + EXPECT_THAT(Scope("outer_inner_vars").Eval("Nested::static_const"), + IsEqual("10")); + EXPECT_THAT(Scope("outer_vars").Eval("Nested::static_const"), IsEqual("20")); + EXPECT_THAT(Scope("vars").Eval("Nested::static_const"), IsEqual("30")); + + EXPECT_THAT(Scope("vars").Eval("::static_const"), + IsError("use of undeclared identifier '::static_const'")); + EXPECT_THAT(Scope("vars").Eval("::Nested::static_const"), + IsError("use of undeclared identifier '::Nested::static_const'")); + + // Evaluate in value context where value is of alised type. + EXPECT_THAT(Scope("my_outer_inner_vars").Eval("static_const"), IsEqual("3")); + EXPECT_THAT(Scope("my_outer_vars").Eval("static_const"), IsEqual("6")); + EXPECT_THAT(Scope("my_vars").Eval("static_const"), IsEqual("9")); + + EXPECT_THAT(Scope("my_outer_inner_vars").Eval("Nested::static_const"), + IsEqual("10")); + EXPECT_THAT(Scope("my_outer_vars").Eval("Nested::static_const"), + IsEqual("20")); + EXPECT_THAT(Scope("my_vars").Eval("Nested::static_const"), IsEqual("30")); + + EXPECT_THAT(Scope("my_outer_inner_vars").Eval("::static_const"), + IsError("use of undeclared identifier '::static_const'")); + EXPECT_THAT(Scope("my_vars").Eval("::Nested::static_const"), + IsError("use of undeclared identifier '::Nested::static_const'")); +} +#endif + +TEST_F(EvalTest, TestBasicTypeDeclaration) { + EXPECT_THAT(Eval("(char)65"), IsEqual("'A'")); + EXPECT_THAT(Eval("(char unsigned)65"), IsEqual("'A'")); + EXPECT_THAT(Eval("(signed char)65"), IsEqual("'A'")); +#ifndef __EMSCRIPTEN__ + if constexpr (sizeof(wchar_t) == 2) { + // Size of "wchar_t" is 2 bytes on Windows. + EXPECT_THAT(Eval("(wchar_t)0x4141"), IsEqual("AA")); + } else { + // Size of "wchar_t" is 4 bytes on Linux. + EXPECT_THAT(Eval("(wchar_t)0x41414141"), IsEqual("AAAA")); + } + EXPECT_THAT(Eval("(char16_t)0x4141"), IsEqual("U+4141")); + EXPECT_THAT(Eval("(char32_t)0x4141"), IsEqual("U+0x00004141")); +#endif + EXPECT_THAT(Eval("(int short)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(short int)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(short)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(unsigned short)-1"), IsEqual("65535")); + EXPECT_THAT(Eval("(short unsigned)-1"), IsEqual("65535")); + EXPECT_THAT(Eval("(int short unsigned)-1"), IsEqual("65535")); + EXPECT_THAT(Eval("(int)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(signed int)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(signed)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(unsigned)-1"), IsEqual("4294967295")); + EXPECT_THAT(Eval("(int unsigned)-1"), IsEqual("4294967295")); + EXPECT_THAT(Eval("(long)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(signed long)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(long int signed)-1"), IsEqual("-1")); + if constexpr (sizeof(long) == 4) { + // Size of "long" is 4 bytes on Windows. + EXPECT_THAT(Eval("(unsigned long)-1"), IsEqual("4294967295")); + EXPECT_THAT(Eval("(int long unsigned)-1"), IsEqual("4294967295")); + } else { + // Size of "long" is 8 bytes on Linux. + EXPECT_THAT(Eval("(unsigned long)-1"), IsEqual("18446744073709551615")); + EXPECT_THAT(Eval("(int long unsigned)-1"), IsEqual("18446744073709551615")); + } + EXPECT_THAT(Eval("(long long)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(long long int)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(int signed long long)-1"), IsEqual("-1")); + EXPECT_THAT(Eval("(long int long unsigned)-1"), + IsEqual("18446744073709551615")); + EXPECT_THAT(Eval("(int long unsigned long)-1"), + IsEqual("18446744073709551615")); + EXPECT_THAT(Eval("(unsigned long long)-1"), IsEqual("18446744073709551615")); + + EXPECT_THAT(Eval("(float)1.5"), IsEqual("1.5")); + EXPECT_THAT(Eval("(double)1.5"), IsEqual("1.5")); +#ifdef _WIN32 + // Casting to "long double" results in "NaN" on Linux. + EXPECT_THAT(Eval("(long double)1.5"), IsEqual("1.5")); + EXPECT_THAT(Eval("(double long)1.5"), IsEqual("1.5")); +#endif + EXPECT_THAT(Eval("(bool)1.5"), IsEqual("true")); + + EXPECT_THAT(Eval("(void*)0"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("(unsigned**)0"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + + EXPECT_THAT( + Eval("(int int)0"), + IsError("cannot combine with previous 'int' declaration specifier\n" + "(int int)0\n" + " ^")); + EXPECT_THAT( + Eval("(char int)0"), + IsError("cannot combine with previous 'char' declaration specifier")); + EXPECT_THAT( + Eval("(int char)0"), + IsError("cannot combine with previous 'int' declaration specifier")); + EXPECT_THAT( + Eval("(long long long)0"), + IsError( + "cannot combine with previous 'long long' declaration specifier")); + EXPECT_THAT( + Eval("(long long double)0"), + IsError( + "cannot combine with previous 'long long' declaration specifier")); + EXPECT_THAT( + Eval("(long double long)0"), + IsError( + "cannot combine with previous 'long double' declaration specifier")); + EXPECT_THAT(Eval("(unsigned long double)0"), + IsError("'double' cannot be signed or unsigned")); + EXPECT_THAT(Eval("(long double signed)0"), + IsError("'long double' cannot be signed or unsigned")); + EXPECT_THAT( + Eval("(short float)0"), + IsError("cannot combine with previous 'short' declaration specifier")); + EXPECT_THAT( + Eval("(unsigned signed)0"), + IsError("cannot combine with previous 'unsigned' declaration specifier")); + EXPECT_THAT( + Eval("(unsigned unsigned)0"), + IsError("cannot combine with previous 'unsigned' declaration specifier")); + EXPECT_THAT(Eval("(signed wchar_t)0"), + IsError("'wchar_t' cannot be signed or unsigned")); + EXPECT_THAT(Eval("(signed char16_t)0"), + IsError("'char16_t' cannot be signed or unsigned")); + EXPECT_THAT(Eval("(signed char32_t)0"), + IsError("'char32_t' cannot be signed or unsigned")); + EXPECT_THAT(Eval("(unsigned float)0"), + IsError("'float' cannot be signed or unsigned")); + EXPECT_THAT(Eval("(unsigned double)0"), + IsError("'double' cannot be signed or unsigned")); + EXPECT_THAT(Eval("(unsigned bool)0"), + IsError("'bool' cannot be signed or unsigned")); + EXPECT_THAT(Eval("(unsigned void)0"), + IsError("'void' cannot be signed or unsigned")); + EXPECT_THAT(Eval("(bool unsigned)0"), + IsError("'bool' cannot be signed or unsigned")); + EXPECT_THAT(Eval("(bool signed)0"), + IsError("'bool' cannot be signed or unsigned")); + + // Error reporting works with other kinds of expression. + EXPECT_THAT( + Eval("static_cast(0)"), + IsError("cannot combine with previous 'int' declaration specifier")); + EXPECT_THAT( + Eval("sizeof(int int)"), + IsError("cannot combine with previous 'int' declaration specifier")); +} + +TEST_F(EvalTest, TestUserTypeDeclaration) { + EXPECT_THAT(Eval("(unsigned mylong)0"), + IsError("cannot combine with previous declaration specifier")); + EXPECT_THAT(Eval("static_cast(0)"), + IsError("two or more data types in declaration of 'type name'")); + EXPECT_THAT(Eval("static_cast(0)"), + IsError("cannot combine with previous declaration specifier")); + // TODO: Should this be a type name error instead? + EXPECT_THAT(Eval("(mylong unsigned)0"), + IsError("undeclared identifier 'mylong'")); +} + +TEST_F(EvalTest, TestTemplateTypes) { + // Template types lookup doesn't work well in the upstream LLDB. + this->compare_with_lldb_ = false; +#ifndef __EMSCRIPTEN__ + // Get the pointer value and use it to check the expressions with lldb-eval. + auto expected = frame_.EvaluateExpression("p").GetValue(); + + for (std::string arg : {"int", "int*", "int**", "int&", "int*&", "double"}) { + EXPECT_THAT(Eval("(T_1<" + arg + ">*)p"), IsEqual(expected)); + EXPECT_THAT(Eval("(::T_1<" + arg + ">*)p"), IsEqual(expected)); + } + EXPECT_THAT(Eval("(T_2*)p"), IsEqual(expected)); + EXPECT_THAT(Eval("(::T_2*)p"), IsEqual(expected)); + EXPECT_THAT(Eval("(T_2*)p"), IsEqual(expected)); + EXPECT_THAT(Eval("(::T_2*)p"), IsEqual(expected)); + EXPECT_THAT(Eval("(T_2, T_1 >*)p"), IsEqual(expected)); + EXPECT_THAT(Eval("(::T_2, T_1 >*)p"), IsEqual(expected)); + EXPECT_THAT(Eval("(T_2 >, T_1 >*)p"), IsEqual(expected)); + EXPECT_THAT(Eval("(::T_2 >, T_1 >*)p"), IsEqual(expected)); + + EXPECT_THAT(Eval("(ns::T_1*)p"), IsEqual(expected)); + EXPECT_THAT(Eval("(::ns::T_1*)p"), IsEqual(expected)); + EXPECT_THAT(Eval("(ns::T_1 >*)p"), IsEqual(expected)); + EXPECT_THAT(Eval("(::ns::T_1 >*)p"), IsEqual(expected)); +#endif + EXPECT_THAT( + Eval("ns::T_1 >::cx"), + XFail(IsError( + "use of undeclared identifier 'ns::T_1 >::cx'"))); + EXPECT_THAT(Eval("T_1::cx"), XFail(IsEqual("24"))); + EXPECT_THAT(Eval("T_1::cx"), XFail(IsEqual("42"))); + EXPECT_THAT(Eval("ns::T_1::cx"), XFail(IsEqual("64"))); + + for (std::string arg : {"int", "int*", "int**", "int&", "int*&"}) { + EXPECT_THAT(Eval("(T_1<" + arg + ">::myint)1.2"), XFail(IsEqual("1.2"))); + EXPECT_THAT(Eval("(::T_1<" + arg + ">::myint)1.2"), XFail(IsEqual("1.2"))); + EXPECT_THAT(Eval("(T_1 >::myint)1.2"), + XFail(IsEqual("1.2"))); + EXPECT_THAT(Eval("(::T_1 >::myint)1.2"), + XFail(IsEqual("1.2"))); + + EXPECT_THAT(Eval("(ns::T_1<" + arg + ">::myint)1.1"), XFail(IsEqual("1"))); + EXPECT_THAT(Eval("(::ns::T_1<" + arg + ">::myint)1.1"), + XFail(IsEqual("1"))); + EXPECT_THAT(Eval("(ns::T_1 >::myint)1.1"), + XFail(IsEqual("1"))); + EXPECT_THAT(Eval("(::ns::T_1 >::myint)1.1"), + XFail(IsEqual("1"))); + } + + EXPECT_THAT( + Eval("(ns::T_1 >::myint)1.1"), + XFail(IsError( + "use of undeclared identifier 'ns::T_1 >::myint'"))); + EXPECT_THAT( + Eval("(::ns::T_1 >::myint)1.1"), + XFail(IsError( + "use of undeclared identifier '::ns::T_1 >::myint'"))); + for (std::string arg : {"int*", "int**", "int&", "int*&"}) { + EXPECT_THAT(Eval("(ns::T_1 >::myint)1.1"), + IsError("use of undeclared identifier 'ns::T_1'")); + EXPECT_THAT(Eval("(::ns::T_1 >::myint)1.1"), + IsError("use of undeclared identifier '::ns::T_1'")); + } + + EXPECT_THAT(Eval("(T_2::myint)1.1f"), + XFail(IsEqual("1.10000002"))); + EXPECT_THAT(Eval("(::T_2::myint)1.1f"), + XFail(IsEqual("1.10000002"))); + EXPECT_THAT(Eval("(T_2::myint)1.1f"), + XFail(IsEqual("1.10000002"))); + EXPECT_THAT(Eval("(::T_2::myint)1.1f"), + XFail(IsEqual("1.10000002"))); + EXPECT_THAT(Eval("(T_2 >, T_1 >::myint)1.1"), + XFail(IsEqual("1.10000002"))); + EXPECT_THAT(Eval("(::T_2 >, T_1 >::myint)1.1"), + XFail(IsEqual("1.10000002"))); +} + +TEST_F(EvalTest, TestTemplateCpp11) { + // Template types lookup doesn't work well in the upstream LLDB. + this->compare_with_lldb_ = false; + + EXPECT_THAT(Eval("(T_1>::myint)1"), XFail(IsEqual("1"))); + EXPECT_THAT(Eval("(T_1>>::myint)2"), XFail(IsEqual("2"))); + EXPECT_THAT(Eval("(T_2>, T_1>::myint)1.5"), + XFail(IsEqual("1.5"))); + + // Here T_1 is a local variable. + EXPECT_THAT(Eval("T_1<2>1"), IsEqual("false")); // (p < 2) > 1 + EXPECT_THAT(Eval("T_1<2>>1"), IsEqual("false")); // (p < 2) >> 1 + // And here it's a template. + EXPECT_THAT(Eval("T_1::cx + 1"), XFail(IsEqual("25"))); +} + +TEST_F(EvalTest, TestTemplateWithNumericArguments) { + // Template types lookup doesn't work well in the upstream LLDB. + this->compare_with_lldb_ = false; + + EXPECT_THAT(Eval("(Allocator<4>*)0"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("(TArray >::ElementType*)0"), + XFail(IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000"))); + // Test C++11's ">>" syntax. + EXPECT_THAT(Eval("(TArray>::ElementType*)0"), + XFail(IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000"))); +} + +#ifndef __EMSCRIPTEN__ +TEST_F(EvalTest, DISABLED_TestValueScope) { + EXPECT_THAT(Scope("var").Eval("x_"), IsEqual("1")); + EXPECT_THAT(Scope("var").Eval("y_"), IsEqual("2.5")); + EXPECT_THAT(Scope("var").Eval("z_"), + IsError("use of undeclared identifier 'z_'")); + + // In "value" scope `this` refers to the scope object. + EXPECT_THAT(Scope("var").Eval("this->y_"), IsEqual("2.5")); + EXPECT_THAT(Scope("var").Eval("(*this).y_"), IsEqual("2.5")); + + // Test for the "artificial" value, i.e. created by the expression. + lldb::SBValue scope_var = frame_.EvaluateExpressionViaDIL( + "(test_scope::Value&)bytes", lldb::eNoDynamicValues); + lldb::SBError error = scope_var.GetError(); + EXPECT_TRUE(scope_var.IsValid()); + EXPECT_TRUE(error.Success()); + + EvaluatorHelper scope(scope_var, true, false); + EXPECT_THAT(scope.Eval("this->y_"), IsEqual("2.5")); + EXPECT_THAT(scope.Eval("(*this).y_"), IsEqual("2.5")); + + EXPECT_THAT(Eval("x_"), IsError("use of undeclared identifier 'x_'")); + EXPECT_THAT(Eval("y_"), IsError("use of undeclared identifier 'y_'")); + EXPECT_THAT(Eval("z_"), IsEqual("3")); + + // In the frame context `this` is not available here. + EXPECT_THAT( + Eval("this->y_"), + IsError("invalid use of 'this' outside of a non-static member function")); + EXPECT_THAT( + Eval("(*this)->y_"), + IsError("invalid use of 'this' outside of a non-static member function")); + + EXPECT_THAT(Scope("var").Eval("this - (test_scope::Value*)this"), + IsEqual("0")); +} + +TEST_F(EvalTest, DISABLED_TestReferenceScope) { + // Member access in "reference" context doesn't work in LLDB. + this->compare_with_lldb_ = false; + + EXPECT_THAT(Scope("var_ref").Eval("x_"), IsEqual("1")); + EXPECT_THAT(Scope("var_ref").Eval("y_"), IsEqual("2.5")); + EXPECT_THAT(Scope("var_ref").Eval("z_"), + IsError("use of undeclared identifier 'z_'")); + EXPECT_THAT(Scope("var_ref").Eval("this"), IsOk()); + EXPECT_THAT(Scope("var_ref").Eval("this->y_"), IsEqual("2.5")); + EXPECT_THAT(Scope("var_ref").Eval("(*this).y_"), IsEqual("2.5")); + EXPECT_THAT(Scope("var_ref").Eval("ValueEnum::B"), IsEqual("B")); + EXPECT_THAT(Scope("var_ref").Eval("static_var"), IsEqual("3.5")); + EXPECT_THAT(Scope("var_ref").Eval("this->static_var"), + IsError("no member named 'static_var' in 'test_scope::Value'")); +} +#endif + +TEST_F(EvalTest, TestBitField) { + EXPECT_THAT(Eval("bf.a"), IsEqual("1023")); + EXPECT_THAT(Eval("bf.b"), IsEqual("9")); + EXPECT_THAT(Eval("bf.c"), IsEqual("false")); + EXPECT_THAT(Eval("bf.d"), IsEqual("true")); + + // Perform an operation to ensure we actually read the value. + EXPECT_THAT(Eval("0 + bf.a"), IsEqual("1023")); + EXPECT_THAT(Eval("0 + bf.b"), IsEqual("9")); + EXPECT_THAT(Eval("0 + bf.c"), IsEqual("0")); + EXPECT_THAT(Eval("0 + bf.d"), IsEqual("1")); + + EXPECT_THAT(Eval("abf.a"), IsEqual("1023")); + EXPECT_THAT(Eval("abf.b"), IsEqual("'\\x0f'")); + EXPECT_THAT(Eval("abf.c"), IsEqual("3")); + + EXPECT_THAT(Eval("abf.a + 0"), IsEqual("1023")); + EXPECT_THAT(Eval("abf.b + 0"), IsEqual("15")); + EXPECT_THAT(Eval("abf.c + 0"), IsEqual("3")); + + // Address-of is not allowed for bit-fields. + EXPECT_THAT(Eval("&bf.a"), IsError("address of bit-field requested")); + EXPECT_THAT(Eval("&(true ? bf.a : bf.a)"), + IsError("address of bit-field requested")); +} + +#ifndef __EMSCRIPTEN__ +TEST_F(EvalTest, DISABLED_TestBitFieldScoped) { + EXPECT_THAT(Scope("bf").Eval("a"), IsEqual("1023")); + EXPECT_THAT(Scope("bf").Eval("b"), IsEqual("9")); + EXPECT_THAT(Scope("bf").Eval("c"), IsEqual("false")); + EXPECT_THAT(Scope("bf").Eval("d"), IsEqual("true")); + EXPECT_THAT(Scope("bf").Eval("0 + a"), IsEqual("1023")); + // TODO: Enable type comparison after fixing bitfield promotion in value + // context. + EXPECT_THAT(Scope("bf").Eval("0 + b"), IsEqual("9", /*compare_types*/ + false)); + EXPECT_THAT(Scope("bf").Eval("0 + c"), IsEqual("0")); + EXPECT_THAT(Scope("bf").Eval("0 + d"), IsEqual("1")); + EXPECT_THAT(Scope("abf").Eval("a"), IsEqual("1023")); + EXPECT_THAT(Scope("abf").Eval("b"), IsEqual("'\\x0f'")); + EXPECT_THAT(Scope("abf").Eval("c"), IsEqual("3")); + EXPECT_THAT(Scope("abf").Eval("0 + a"), IsEqual("1023")); + EXPECT_THAT(Scope("abf").Eval("0 + b"), IsEqual("15")); + EXPECT_THAT(Scope("abf").Eval("0 + c"), IsEqual("3")); +} +#endif + +TEST_F(EvalTest, TestBitFieldPromotion) { + EXPECT_THAT(Eval("bf.b - 10"), IsEqual("-1")); + EXPECT_THAT(Eval("bf.e - 2"), IsEqual("-1")); + EXPECT_THAT(Eval("bf.f - 2"), IsEqual("4294967295")); + EXPECT_THAT(Eval("bf.g - 2"), IsEqual("-1")); + EXPECT_THAT(Eval("bf.h - 2"), IsEqual("-1")); + EXPECT_THAT(Eval("bf.i - 2"), XFail(IsEqual("18446744073709551615"))); + EXPECT_THAT(Eval("bf.g - bf.b"), IsEqual("-8")); + + EXPECT_THAT(Eval("-(true ? bf.b : bf.a)"), IsEqual("-9")); + EXPECT_THAT(Eval("-(true ? bf.b : bf.e)"), IsEqual("-9")); + EXPECT_THAT(Eval("-(true ? bf.b : bf.f)"), IsEqual("4294967287")); + EXPECT_THAT(Eval("-(true ? bf.b : bf.g)"), IsEqual("4294967287")); + EXPECT_THAT(Eval("-(true ? bf.b : bf.h)"), IsEqual("-9")); + + if (HAS_METHOD(lldb::SBType, GetEnumerationIntegerType())) { + EXPECT_THAT(Eval("bf.j - 2"), XFail(IsEqual("4294967295"))); + EXPECT_THAT(Eval("-(true ? bf.b : bf.j)"), IsEqual("4294967287")); + EXPECT_THAT(Eval("-(true ? bf.e : bf.j)"), IsEqual("4294967295")); + } + + // TODO: Repeat tests in the value context once the bitfield information is + // correctly retrieved from identifier lookup. +} + +#ifndef __EMSCRIPTEN__ +TEST_F(EvalTest, TestBitFieldWithSideEffects) { + this->compare_with_lldb_ = false; + this->allow_side_effects_ = true; + + EXPECT_THAT(Eval("bf.b -= 10"), IsEqual("15")); + EXPECT_THAT(Eval("bf.e -= 10"), IsEqual("-9")); + EXPECT_THAT(Eval("bf.e++"), XFail(IsEqual("-9"))); + EXPECT_THAT(Eval("++bf.e"), IsEqual("-7")); + + // TODO: Enable test once the issue is fixed: + // EXPECT_THAT(Eval("bf.b++"), IsEqual("15")); +} + +TEST_F(EvalTest, DISABLED_TestContextVariables) { + // Context variables don't exist yet. + EXPECT_THAT(EvalWithContext("$var", vars_), + IsError("use of undeclared identifier '$var'")); + EXPECT_THAT(Scope("s").EvalWithContext("$var", vars_), + IsError("use of undeclared identifier '$var'")); + + EXPECT_TRUE(CreateContextVariable("$var", "13")); + EXPECT_THAT(EvalWithContext("$var", vars_), IsEqual("13")); + EXPECT_THAT(EvalWithContext("$var + 2", vars_), IsEqual("15")); + EXPECT_THAT(EvalWithContext("$var - s.a", vars_), IsEqual("3")); + EXPECT_THAT(EvalWithContext("var", vars_), + IsError("use of undeclared identifier 'var'")); + EXPECT_THAT(Scope("s").EvalWithContext("$var", vars_), IsEqual("13")); + EXPECT_THAT(Scope("s").EvalWithContext("$var + 2", vars_), IsEqual("15")); + EXPECT_THAT(Scope("s").EvalWithContext("$var - a", vars_), IsEqual("3")); + EXPECT_THAT(Scope("s").EvalWithContext("var", vars_), + IsError("use of undeclared identifier 'var'")); + + // Context variable is a pointer. + EXPECT_TRUE(CreateContextVariable("$ptr", "s.ptr")); + EXPECT_THAT(EvalWithContext("$ptr == s.ptr", vars_), IsEqual("true")); + EXPECT_THAT(EvalWithContext("*$ptr", vars_), IsEqual("'h'")); + EXPECT_THAT(EvalWithContext("$ptr[1]", vars_), IsEqual("'e'")); + EXPECT_THAT(Scope("s").EvalWithContext("$ptr == ptr", vars_), + IsEqual("true")); + EXPECT_THAT(Scope("s").EvalWithContext("*$ptr", vars_), IsEqual("'h'")); + EXPECT_THAT(Scope("s").EvalWithContext("$ptr[1]", vars_), IsEqual("'e'")); + + EXPECT_THAT(EvalWithContext("$var + *$ptr", vars_), IsEqual("117")); + EXPECT_THAT(Scope("s").EvalWithContext("$var + *$ptr", vars_), + IsEqual("117")); +} + +TEST_F(EvalTest, DISABLED_TestContextVariablesSubset) { + // All context variables that are created in this test are visible by LLDB. + // Disable comparisons with LLDB to test subsets of created context variables. + this->compare_with_lldb_ = false; + + EXPECT_TRUE(CreateContextVariable("$var", "13")); + EXPECT_TRUE(CreateContextVariable("$ptr", "s.ptr")); + + // Evaluate without the context. + EXPECT_THAT(Eval("$var"), IsError("use of undeclared identifier '$var'")); + EXPECT_THAT(Eval("$var + 1"), IsError("use of undeclared identifier '$var'")); + EXPECT_THAT(Scope("s").Eval("$var"), + IsError("use of undeclared identifier '$var'")); + EXPECT_THAT(Scope("s").Eval("$var + 1"), + IsError("use of undeclared identifier '$var'")); + + std::unordered_map var; + std::unordered_map ptr; + var.emplace("$var", vars_["$var"]); + ptr.emplace("$ptr", vars_["$ptr"]); + + EXPECT_THAT(EvalWithContext("$var + 0", var), IsEqual("13")); + EXPECT_THAT(EvalWithContext("*$ptr", var), + IsError("use of undeclared identifier '$ptr'")); + EXPECT_THAT(EvalWithContext("$var + *$ptr", var), + IsError("use of undeclared identifier '$ptr'")); + EXPECT_THAT(EvalWithContext("$var + *$ptr", ptr), + IsError("use of undeclared identifier '$var'")); + EXPECT_THAT(Scope("s").EvalWithContext("$var + 0", var), IsEqual("13")); + EXPECT_THAT(Scope("s").EvalWithContext("*$ptr", var), + IsError("use of undeclared identifier '$ptr'")); + EXPECT_THAT(Scope("s").EvalWithContext("$var + *$ptr", var), + IsError("use of undeclared identifier '$ptr'")); + EXPECT_THAT(Scope("s").EvalWithContext("$var + *$ptr", ptr), + IsError("use of undeclared identifier '$var'")); +} +#endif + +TEST_F(EvalTest, TestScopedEnum) { + EXPECT_THAT(Eval("enum_foo"), IsEqual("kFoo")); + EXPECT_THAT(Eval("enum_bar"), IsEqual("kBar")); + + EXPECT_THAT(Eval("enum_foo == enum_foo"), IsEqual("true")); + EXPECT_THAT(Eval("enum_foo != enum_foo"), IsEqual("false")); + EXPECT_THAT(Eval("enum_foo == enum_bar"), IsEqual("false")); + EXPECT_THAT(Eval("enum_foo < enum_bar"), IsEqual("true")); + + EXPECT_THAT(Eval("enum_foo == ScopedEnum::kFoo"), IsEqual("true")); + EXPECT_THAT(Eval("enum_foo == ScopedEnum::kBar"), IsEqual("false")); + EXPECT_THAT(Eval("enum_foo != ScopedEnum::kBar"), IsEqual("true")); + EXPECT_THAT(Eval("enum_foo < ScopedEnum::kBar"), IsEqual("true")); + EXPECT_THAT(Eval("enum_foo < enum_neg"), IsEqual("false")); + EXPECT_THAT(Eval("enum_neg < enum_bar"), IsEqual("true")); + + EXPECT_THAT(Eval("(ScopedEnum)0"), IsEqual("kFoo")); + EXPECT_THAT(Eval("(ScopedEnum)1"), IsEqual("kBar")); + EXPECT_THAT(Eval("(ScopedEnum)0.1"), IsEqual("kFoo")); + EXPECT_THAT(Eval("(ScopedEnum)1.1"), IsEqual("kBar")); + EXPECT_THAT(Eval("(ScopedEnum)-1"), IsOk()); + EXPECT_THAT(Eval("(ScopedEnum)-1.1"), XFail(IsOk())); + EXPECT_THAT(Eval("(ScopedEnum)256"), IsOk()); + EXPECT_THAT(Eval("(ScopedEnum)257"), IsOk()); + EXPECT_THAT(Eval("(ScopedEnum)false"), IsEqual("kFoo")); + EXPECT_THAT(Eval("(ScopedEnum)true"), IsEqual("kBar")); + + EXPECT_THAT(Eval("(int)enum_foo"), IsEqual("0")); + EXPECT_THAT(Eval("(int)enum_neg"), IsEqual("-1")); + EXPECT_THAT(Eval("(unsigned short)enum_neg"), IsEqual("65535")); + EXPECT_THAT(Eval("(short)ScopedEnum::kBar"), IsEqual("1")); + EXPECT_THAT(Eval("(short*)enum_neg"), + XFail(IsEqual(Is32Bit() ? "0xffffffff" : "0xffffffffffffffff"))); + EXPECT_THAT(Eval("(char*)enum_u8_bar"), + IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); + EXPECT_THAT(Eval("(float)enum_bar"), IsEqual("1")); + EXPECT_THAT(Eval("(float)enum_foo"), IsEqual("0")); + EXPECT_THAT(Eval("(float)enum_neg"), IsEqual("-1")); + EXPECT_THAT(Eval("(double)enum_neg"), IsEqual("-1")); + EXPECT_THAT(Eval("(bool)enum_foo"), IsEqual("false")); + EXPECT_THAT(Eval("(bool)enum_bar"), IsEqual("true")); + EXPECT_THAT(Eval("(bool)enum_neg"), IsEqual("true")); + EXPECT_THAT(Eval("(double)ScopedEnumUInt8::kBar"), IsEqual("1")); + EXPECT_THAT(Eval("(ScopedEnum)ScopedEnum::kBar"), IsEqual("kBar")); + + // TODO: Enable the following test once the underlying enumeration type + // becomes fully available. Information about underlying enumeration type + // isn't available in LLDB 11 or lower, which are currently used by GitHub + // actions. + // EXPECT_THAT(Eval("(int)(ScopedEnumUInt8)enum_neg"), IsEqual("255")); +} + +TEST_F(EvalTest, TestScopedEnumArithmetic) { + if (!HAS_METHOD(lldb::SBType, IsScopedEnumerationType())) { + GTEST_SKIP(); + } + + EXPECT_THAT(Eval("enum_foo == 1"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("enum_foo + 1"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("enum_foo * 2"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("enum_bar / 2"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("enum_foo % 2"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("enum_bar & 2"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("enum_bar | 0x01"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("enum_bar ^ 0b11"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("enum_bar >> 1"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("enum_bar << 1"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("1 >> enum_bar"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("1 << enum_bar"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("(int*)1 + enum_foo"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("(int*)5 - enum_foo"), + IsError("invalid operands to binary expression")); + EXPECT_THAT(Eval("+enum_foo"), IsError("invalid argument type")); + EXPECT_THAT(Eval("-enum_foo"), IsError("invalid argument type")); + EXPECT_THAT(Eval("~enum_foo"), IsError("invalid argument type")); + EXPECT_THAT(Eval("!enum_foo"), + IsError("invalid argument type 'ScopedEnum' to unary expression\n" + "!enum_foo\n" + "^")); +} + +TEST_F(EvalTest, TestScopedEnumWithUnderlyingType) { + EXPECT_THAT(Eval("(ScopedEnumUInt8)-1"), IsOk()); + EXPECT_THAT(Eval("(ScopedEnumUInt8)256"), IsEqual("kFoo")); + EXPECT_THAT(Eval("(ScopedEnumUInt8)257"), IsEqual("kBar")); +} + +TEST_F(EvalTest, TestUnscopedEnum) { + EXPECT_THAT(Eval("enum_one"), IsEqual("kOne")); + EXPECT_THAT(Eval("enum_two"), IsEqual("kTwo")); + + EXPECT_THAT(Eval("enum_one == enum_one"), IsEqual("true")); + EXPECT_THAT(Eval("enum_one != enum_one"), IsEqual("false")); + EXPECT_THAT(Eval("enum_one == enum_two"), IsEqual("false")); + EXPECT_THAT(Eval("enum_one < enum_two"), IsEqual("true")); + + EXPECT_THAT(Eval("enum_one == UnscopedEnum::kOne"), IsEqual("true")); + EXPECT_THAT(Eval("enum_one == UnscopedEnum::kTwo"), IsEqual("false")); + EXPECT_THAT(Eval("enum_one != UnscopedEnum::kTwo"), IsEqual("true")); + EXPECT_THAT(Eval("enum_one < UnscopedEnum::kTwo"), IsEqual("true")); + + EXPECT_THAT(Eval("(UnscopedEnum)0"), IsEqual("kZero")); + EXPECT_THAT(Eval("(UnscopedEnum)1"), IsEqual("kOne")); + EXPECT_THAT(Eval("(UnscopedEnum)0.1"), IsEqual("kZero")); + EXPECT_THAT(Eval("(UnscopedEnum)1.1"), IsEqual("kOne")); + EXPECT_THAT(Eval("(UnscopedEnum)-1"), IsOk()); + EXPECT_THAT(Eval("(UnscopedEnum)256"), IsOk()); + EXPECT_THAT(Eval("(UnscopedEnum)257"), IsOk()); + EXPECT_THAT(Eval("(UnscopedEnum)false"), IsEqual("kZero")); + EXPECT_THAT(Eval("(UnscopedEnum)true"), IsEqual("kOne")); + EXPECT_THAT(Eval("(UnscopedEnum)(UnscopedEnum)0"), IsEqual("kZero")); + EXPECT_THAT(Eval("(void*)(UnscopedEnum)1"), + IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); + + bool compare_types = HAS_METHOD(lldb::SBType, GetEnumerationIntegerType()); + + EXPECT_THAT(Eval("enum_one == 1"), IsEqual("true")); + EXPECT_THAT(Eval("enum_one + 1"), IsEqual("2", compare_types)); + EXPECT_THAT(Eval("enum_one * 2"), IsEqual("2", compare_types)); + EXPECT_THAT(Eval("enum_two / 2"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("enum_one % 2"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("enum_two & 2"), IsEqual("2", compare_types)); + EXPECT_THAT(Eval("enum_two | 0x01"), IsEqual("3", compare_types)); + EXPECT_THAT(Eval("enum_two ^ 0b11"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("enum_two >> 1"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("enum_two << 1"), IsEqual("4", compare_types)); + EXPECT_THAT(Eval("8 >> enum_two"), IsEqual("2")); + EXPECT_THAT(Eval("1 << enum_two"), IsEqual("4")); + // TODO: Enable type comparison after the bug is addressed in LLDB. + EXPECT_THAT(Eval("UnscopedEnumUInt8::kTwoU8 << 1"), + IsEqual("4", /*compare_types*/ false)); + EXPECT_THAT(Eval("UnscopedEnumInt8::kTwo8 << 1U"), IsEqual("4")); + EXPECT_THAT(Eval("+enum_one"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("!enum_one"), IsEqual("false")); + EXPECT_THAT(Eval("(int*)1 + enum_one"), + IsEqual(Is32Bit() ? "0x00000005" : "0x0000000000000005")); + EXPECT_THAT(Eval("(int*)5 - enum_one"), + IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); + + // Use references. + EXPECT_THAT(Eval("enum_one_ref"), IsEqual("kOne")); + EXPECT_THAT(Eval("enum_two_ref"), IsEqual("kTwo")); + EXPECT_THAT(Eval("enum_one_ref == enum_one"), IsEqual("true")); + EXPECT_THAT(Eval("enum_one_ref != enum_one"), IsEqual("false")); + EXPECT_THAT(Eval("enum_one_ref == enum_two"), IsEqual("false")); + EXPECT_THAT(Eval("enum_one_ref < enum_two"), IsEqual("true")); + EXPECT_THAT(Eval("enum_one_ref == UnscopedEnum::kOne"), IsEqual("true")); + EXPECT_THAT(Eval("enum_one_ref == UnscopedEnum::kTwo"), IsEqual("false")); + EXPECT_THAT(Eval("enum_one_ref != UnscopedEnum::kTwo"), IsEqual("true")); + EXPECT_THAT(Eval("enum_one_ref < UnscopedEnum::kTwo"), IsEqual("true")); + EXPECT_THAT(Eval("enum_one_ref == 1"), IsEqual("true")); + EXPECT_THAT(Eval("enum_one_ref + 1"), IsEqual("2", compare_types)); + EXPECT_THAT(Eval("enum_one_ref * 2"), IsEqual("2", compare_types)); + EXPECT_THAT(Eval("enum_two_ref / 2"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("enum_one_ref % 2"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("enum_two_ref & 2"), IsEqual("2", compare_types)); + EXPECT_THAT(Eval("enum_two_ref | 0x01"), IsEqual("3", compare_types)); + EXPECT_THAT(Eval("enum_two_ref ^ 0b11"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("enum_two_ref >> 1"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("enum_two_ref << 1"), IsEqual("4", compare_types)); + EXPECT_THAT(Eval("8 >> enum_two_ref"), IsEqual("2")); + EXPECT_THAT(Eval("1 << enum_two_ref"), IsEqual("4")); + EXPECT_THAT(Eval("+enum_one_ref"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("!enum_one_ref"), IsEqual("false")); + EXPECT_THAT(Eval("(int*)1 + enum_one_ref"), + IsEqual(Is32Bit() ? "0x00000005" : "0x0000000000000005")); + EXPECT_THAT(Eval("(int*)5 - enum_one_ref"), + IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); + + EXPECT_THAT(Eval("(long long)UnscopedEnum::kTwo"), IsEqual("2")); + EXPECT_THAT(Eval("(unsigned int)enum_one"), IsEqual("1")); + EXPECT_THAT(Eval("(short*)UnscopedEnumUInt8::kTwoU8"), + IsEqual(Is32Bit() ? "0x00000002" : "0x0000000000000002")); + EXPECT_THAT(Eval("(float)UnscopedEnum::kOne"), IsEqual("1")); + EXPECT_THAT(Eval("(float)enum_two"), IsEqual("2")); + EXPECT_THAT(Eval("(double)enum_one"), IsEqual("1")); + EXPECT_THAT(Eval("(bool)enum_two"), IsEqual("true")); + EXPECT_THAT(Eval("(bool)(UnscopedEnum)0"), IsEqual("false")); + EXPECT_THAT(Eval("(bool)(UnscopedEnum)-1"), IsEqual("true")); +} + +TEST_F(EvalTest, TestUnscopedEnumNegation) { + // Sometimes LLDB is confused about signedness and uses "unsigned int" as an + // underlying type. Technically, underlying type for the unscoped enum is + // implementation defined, but it should convert to "int" nonetheless. + this->compare_with_lldb_ = false; + + bool compare_types = HAS_METHOD(lldb::SBType, GetEnumerationIntegerType()); + EXPECT_THAT(Eval("-enum_one"), XFail(IsEqual("-1", compare_types))); + EXPECT_THAT(Eval("-enum_one_ref"), XFail(IsEqual("-1", compare_types))); + EXPECT_THAT(Eval("-(UnscopedEnumEmpty)1"), IsOk(compare_types)); +} + +TEST_F(EvalTest, TestUnscopedEnumWithUnderlyingType) { + EXPECT_THAT(Eval("(UnscopedEnumUInt8)-1"), IsOk()); + EXPECT_THAT(Eval("(UnscopedEnumUInt8)256"), IsEqual("kZeroU8")); + EXPECT_THAT(Eval("(UnscopedEnumUInt8)257"), IsEqual("kOneU8")); + + EXPECT_THAT(Eval("(UnscopedEnumInt8)-2.1"), XFail(IsOk())); + EXPECT_THAT(Eval("(int)enum_neg_8"), IsEqual("-1")); + EXPECT_THAT(Eval("enum_neg_8 < enum_one_8"), IsEqual("true")); + EXPECT_THAT(Eval("enum_neg_8 > enum_one_8"), IsEqual("false")); +} + +TEST_F(EvalTest, TestUnscopedEnumEmpty) { + EXPECT_THAT(Eval("(UnscopedEnumEmpty)1"), IsOk()); + EXPECT_THAT(Eval("(int)enum_empty"), IsOk()); +} + +TEST_F(EvalTest, TestTernaryOperator) { + EXPECT_THAT(Eval("true ? c : c"), IsEqual("'\\x02'")); + EXPECT_THAT(Eval("true ? c : (char)1"), IsEqual("'\\x02'")); + EXPECT_THAT(Eval("true ? c : 1"), IsEqual("2")); + EXPECT_THAT(Eval("false ? 1 : c"), IsEqual("2")); + + bool compare_types = HAS_METHOD(lldb::SBType, GetEnumerationIntegerType()); + EXPECT_THAT(Eval("false ? a_enum : EnumA::kOneA"), IsEqual("kOneA")); + EXPECT_THAT(Eval("false ? b_enum : a_enum"), IsEqual("2", compare_types)); + EXPECT_THAT(Eval("false ? c : b_enum"), IsEqual("1", compare_types)); + EXPECT_THAT(Eval("false ? b_enum : c"), IsEqual("2", compare_types)); + + EXPECT_THAT(Eval("true ? (int*)15 : 0"), + IsEqual(Is32Bit() ? "0x0000000f" : "0x000000000000000f")); + EXPECT_THAT(Eval("true ? 0 : (int*)15"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("true ? (int*)15 : nullptr"), + IsEqual(Is32Bit() ? "0x0000000f" : "0x000000000000000f")); + EXPECT_THAT(Eval("true ? nullptr : (int*)15"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + + EXPECT_THAT(Eval("true ? nullptr : 0"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + + EXPECT_THAT(Eval("+(true ? arr2 : arr2)"), IsOk()); + EXPECT_THAT(Eval("true ? arr2 : arr3"), IsOk()); + EXPECT_THAT(Eval("true ? arr2 : 0"), IsOk()); + EXPECT_THAT(Eval("true ? 0 : arr2"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("true ? arr2 : nullptr"), IsOk()); + EXPECT_THAT(Eval("true ? nullptr : arr2"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("true ? arr2 : (int*)15"), IsOk()); + EXPECT_THAT(Eval("true ? (int*)15 : arr2"), + IsEqual(Is32Bit() ? "0x0000000f" : "0x000000000000000f")); + + EXPECT_THAT(Eval("&(true ? arr2 : arr2)"), IsOk()); + + EXPECT_THAT(Eval("true ? t : 1"), + IsError("incompatible operand types ('T' and 'int')\n" + "true ? t : 1\n" + " ^")); + EXPECT_THAT(Eval("true ? 1 : t"), + IsError("incompatible operand types ('int' and 'T')\n" + "true ? 1 : t\n" + " ^")); + EXPECT_THAT(Eval("true ? (int*)15 : 1"), + IsError("incompatible operand types ('int *' and 'int')\n" + "true ? (int*)15 : 1\n" + " ^")); + EXPECT_THAT(Eval("true ? arr2 : dbl_arr"), + IsError("incompatible operand types ('int *' and 'double *')\n" + "true ? arr2 : dbl_arr\n" + " ^")); + + EXPECT_THAT(Eval("&(true ? *pi : 0)"), + IsError("cannot take the address of an rvalue of type 'int'\n" + "&(true ? *pi : 0)\n" + "^")); + EXPECT_THAT(Eval("&(true ? arr2 : pi)"), + IsError("cannot take the address of an rvalue of type 'int *'\n" + "&(true ? arr2 : pi)\n" + "^")); + EXPECT_THAT(Eval("&(true ? arr2 : arr3)"), + IsError("cannot take the address of an rvalue of type 'int *'\n" + "&(true ? arr2 : arr3)\n" + "^")); + + EXPECT_THAT(Eval("true ? nullptr : pi"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("true ? pi : nullptr"), IsOk()); + EXPECT_THAT(Eval("false ? nullptr : pi"), IsOk()); + EXPECT_THAT(Eval("false ? pi : nullptr"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + + // Use pointers and arrays in bool context. + EXPECT_THAT(Eval("pi ? 1 : 2"), IsEqual("1")); + EXPECT_THAT(Eval("arr2 ? 1 : 2"), IsEqual("1")); + + GTEST_SKIP() << "Segfault when retrieving result value in the matcher"; + EXPECT_THAT(Eval("true ? 0 : nullptr"), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(Eval("nullptr ? 1 : 2"), IsEqual("2")); +} + +TEST_F(EvalTest, TestSizeOf) { + EXPECT_THAT(Eval("sizeof(int)"), IsEqual("4")); + EXPECT_THAT(Eval("sizeof(int&)"), IsEqual("4")); + EXPECT_THAT(Eval("sizeof(int*)"), IsEqual(Is32Bit() ? "4" : "8")); + EXPECT_THAT(Eval("sizeof(int***)"), IsEqual(Is32Bit() ? "4" : "8")); + + EXPECT_THAT(Eval("sizeof(i)"), IsEqual("4")); + EXPECT_THAT(Eval("sizeof i "), IsEqual("4")); + EXPECT_THAT(Eval("sizeof p "), IsEqual(Is32Bit() ? "4" : "8")); + EXPECT_THAT(Eval("sizeof(arr)"), IsEqual("12")); + EXPECT_THAT(Eval("sizeof arr "), IsEqual("12")); + EXPECT_THAT(Eval("sizeof(arr + 1)"), IsEqual(Is32Bit() ? "4" : "8")); + EXPECT_THAT(Eval("sizeof arr + 1 "), IsEqual("13")); + + EXPECT_THAT(Eval("sizeof(foo)"), IsEqual("8")); + EXPECT_THAT(Eval("sizeof foo "), IsEqual("8")); + EXPECT_THAT(Eval("sizeof(SizeOfFoo)"), IsEqual("8")); + + // Makes sure that the expression isn't parsed as two types `i` and `i`. + EXPECT_THAT(Eval("sizeof(i < i > i)"), IsEqual("1")); + + EXPECT_THAT( + Eval("sizeof(int & *)"), + IsError( + "'type name' declared as a pointer to a reference of type 'int &'\n" + "sizeof(int & *)\n" + " ^")); + + EXPECT_THAT(Eval("sizeof(intt + 1)"), + IsError("use of undeclared identifier 'intt'\n" + "sizeof(intt + 1)\n" + " ^")); +} + +TEST_F(EvalTest, TestBuiltinFunction_Log2) { + // LLDB doesn't support `__log2` intrinsic function. + this->compare_with_lldb_ = false; + + // Note: Visual Studio debugger gives an error in this case: + // + // > __log(0) + // Attempt to calculate __log2(0). + // + EXPECT_THAT(Eval("__log2(0)"), IsEqual("4294967295")); + EXPECT_THAT(Eval("__log2(1LL << 32)"), IsEqual("4294967295")); + + EXPECT_THAT(Eval("__log2(1)"), IsEqual("0")); + EXPECT_THAT(Eval("__log2(8)"), IsEqual("3")); + EXPECT_THAT(Eval("__log2(12345)"), IsEqual("13")); + EXPECT_THAT(Eval("__log2(-1)"), IsEqual("31")); + EXPECT_THAT(Eval("__log2((1LL << 32)+128)"), IsEqual("7")); + + EXPECT_THAT(Eval("__log2(32.3f)"), IsEqual("5")); + EXPECT_THAT(Eval("__log2(12345.12345)"), IsEqual("13")); + + EXPECT_THAT(Eval("__log2(c_enum)"), IsEqual("7")); + EXPECT_THAT(Eval("__log2(cxx_enum)"), IsEqual("7")); + EXPECT_THAT(Eval("__log2(c_enum_ref)"), IsEqual("7")); + EXPECT_THAT(Eval("__log2(cxx_enum_ref)"), IsEqual("7")); + EXPECT_THAT(Eval("__log2(CEnum::kFoo)"), IsEqual("7")); + EXPECT_THAT(Eval("__log2(CxxEnum::kFoo)"), IsEqual("7")); + + EXPECT_THAT(Eval("__log2(foo)"), + IsError("no known conversion from 'Foo' to 'unsigned int'\n" + "__log2(foo)\n" + " ^")); + + EXPECT_THAT(Eval("__log2()"), + IsError("no matching function for call to '__log2': requires 1 " + "argument(s), but 0 argument(s) were provided\n" + "__log2()\n" + "^")); + + EXPECT_THAT(Eval("1 + __log2(1, 2)"), + IsError("no matching function for call to '__log2': requires 1 " + "argument(s), but 2 argument(s) were provided\n" + "1 + __log2(1, 2)\n" + " ^")); + + EXPECT_THAT(Eval("dummy(1)"), + IsError("function 'dummy' is not a supported builtin intrinsic")); + EXPECT_THAT( + Eval("::dummy(1)"), + IsError("function '::dummy' is not a supported builtin intrinsic")); + EXPECT_THAT( + Eval("ns::dummy(1)"), + IsError("function 'ns::dummy' is not a supported builtin intrinsic")); + EXPECT_THAT( + Eval("::ns::dummy(1)"), + IsError("function '::ns::dummy' is not a supported builtin intrinsic")); +} + +TEST_F(EvalTest, DISABLED_TestPrefixIncDec) { + EXPECT_THAT(Eval("++1"), IsError("expression is not assignable")); + EXPECT_THAT(Eval("--i"), + IsError("side effects are not supported in this context")); + +#ifndef __EMSCRIPTEN__ + ASSERT_TRUE(CreateContextVariableArray("int", "$arr", "{1,2,3}")); + EXPECT_THAT(EvalWithContext("++$arr", vars_), + IsError("cannot increment value of type 'int[3]'")); + + ASSERT_TRUE(CreateContextVariable("$enum_foo", "ScopedEnum::kFoo")); + EXPECT_THAT(EvalWithContext("++$enum_foo", vars_), + IsError("cannot increment expression of enum type 'ScopedEnum'")); + + ASSERT_TRUE(CreateContextVariable("$i", "1")); + EXPECT_THAT(EvalWithContext("++$i", vars_), IsEqual("2")); + EXPECT_THAT(EvalWithContext("++$i + 1", vars_), IsEqual("4")); + EXPECT_THAT(EvalWithContext("--$i", vars_), IsEqual("2")); + EXPECT_THAT(EvalWithContext("--$i - 1", vars_), IsEqual("0")); + + ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); + EXPECT_THAT(EvalWithContext("++$f", vars_), IsEqual("2.5")); + EXPECT_THAT(EvalWithContext("++$f + 1", vars_), IsEqual("4.5")); + EXPECT_THAT(EvalWithContext("--$f", vars_), IsEqual("2.5")); + EXPECT_THAT(EvalWithContext("--$f - 1", vars_), IsEqual("0.5")); + + ASSERT_TRUE(CreateContextVariable("$d", "1.5")); + EXPECT_THAT(EvalWithContext("++$d", vars_), IsEqual("2.5")); + EXPECT_THAT(EvalWithContext("++$d + 1", vars_), IsEqual("4.5")); + EXPECT_THAT(EvalWithContext("--$d", vars_), IsEqual("2.5")); + EXPECT_THAT(EvalWithContext("--$d - 1", vars_), IsEqual("0.5")); + + ASSERT_TRUE(CreateContextVariable("$p", "(int*)4")); + EXPECT_THAT(EvalWithContext("++$p", vars_), + IsEqual(Is32Bit() ? "0x00000008" : "0x0000000000000008")); + EXPECT_THAT(EvalWithContext("++$p + 1", vars_), + IsEqual(Is32Bit() ? "0x00000010" : "0x0000000000000010")); + EXPECT_THAT(EvalWithContext("--$p", vars_), + IsEqual(Is32Bit() ? "0x00000008" : "0x0000000000000008")); + EXPECT_THAT(EvalWithContext("--$p - 1", vars_), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); +#endif +} + +TEST_F(EvalTest, DISABLED_TestPostfixIncDec) { + EXPECT_THAT(Eval("1++"), IsError("expression is not assignable")); + EXPECT_THAT(Eval("i--"), + IsError("side effects are not supported in this context")); + +#ifndef __EMSCRIPTEN__ + ASSERT_TRUE(CreateContextVariableArray("int", "$arr", "{1,2,3}")); + EXPECT_THAT(EvalWithContext("$arr--", vars_), + IsError("cannot decrement value of type 'int[3]'")); + + ASSERT_TRUE(CreateContextVariable("$enum_foo", "ScopedEnum::kFoo")); + EXPECT_THAT(EvalWithContext("$enum_foo++", vars_), + IsError("cannot increment expression of enum type 'ScopedEnum'")); + + ASSERT_TRUE(CreateContextVariable("$i", "1")); + EXPECT_THAT(EvalWithContext("$i++", vars_), IsEqual("1")); + EXPECT_THAT(EvalWithContext("$i", vars_), IsEqual("2")); + EXPECT_THAT(EvalWithContext("$i++ + 1", vars_), IsEqual("3")); + EXPECT_THAT(EvalWithContext("$i--", vars_), IsEqual("3")); + EXPECT_THAT(EvalWithContext("$i-- - 1", vars_), IsEqual("1")); + + ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); + EXPECT_THAT(EvalWithContext("$f++", vars_), IsEqual("1.5")); + EXPECT_THAT(EvalWithContext("$f", vars_), IsEqual("2.5")); + EXPECT_THAT(EvalWithContext("$f++ + 1", vars_), IsEqual("3.5")); + EXPECT_THAT(EvalWithContext("$f--", vars_), IsEqual("3.5")); + EXPECT_THAT(EvalWithContext("$f-- - 1", vars_), IsEqual("1.5")); + + ASSERT_TRUE(CreateContextVariable("$d", "1.5")); + EXPECT_THAT(EvalWithContext("$d++", vars_), IsEqual("1.5")); + EXPECT_THAT(EvalWithContext("$d++ + 1", vars_), IsEqual("3.5")); + EXPECT_THAT(EvalWithContext("$d--", vars_), IsEqual("3.5")); + EXPECT_THAT(EvalWithContext("$d-- - 1", vars_), IsEqual("1.5")); + + ASSERT_TRUE(CreateContextVariable("$p", "(int*)4")); + EXPECT_THAT(EvalWithContext("$p++", vars_), + IsEqual(Is32Bit() ? "0x00000004" : "0x0000000000000004")); + EXPECT_THAT(EvalWithContext("$p++ + 1", vars_), + IsEqual(Is32Bit() ? "0x0000000c" : "0x000000000000000c")); + EXPECT_THAT(EvalWithContext("$p--", vars_), + IsEqual(Is32Bit() ? "0x0000000c" : "0x000000000000000c")); + EXPECT_THAT(EvalWithContext("$p-- - 1", vars_), + IsEqual(Is32Bit() ? "0x00000004" : "0x0000000000000004")); +#endif +} + +#ifndef __EMSCRIPTEN__ +TEST_F(EvalTest, TestDereferencedType) { + // When dereferencing the value LLDB also removes the typedef and the + // resulting value has the canonical type. We have to mitigate that, because + // having the correct type is important for visualization logic. + // + // In this example we have: + // + // ``` + // struct TTuple { ... }; + // using TPair = TTuple; + // const TPair& p_ref = ...; + // const TPair* p_ptr = ...; + // ``` + // + // The expression evaluator removes the references from all values and we must + // verify that the resulting type is correct. + + for (auto &&expr : {"p_ref", "*p_ptr"}) { + // Directly evaluate the expression and verify the types. + auto ret = Eval(expr); + + ASSERT_TRUE(ret.lldb_DIL_error.Success()); + ASSERT_TRUE(ret.lldb_value.has_value()); + + ASSERT_STREQ(ret.lldb_DIL_value.GetTypeName(), "TPair"); + ASSERT_STREQ(ret.lldb_value.value().GetTypeName(), "TPair"); + } +} +#endif + +TEST_F(EvalTest, TestMemberFunctionCall) { + // LLDB supports function calls, so disable it. + this->compare_with_lldb_ = false; + + EXPECT_THAT(Eval("c.m()"), + IsError("member function calls are not supported")); +} + +TEST_F(EvalTest, DISABLED_TestAssignment) { + EXPECT_THAT(Eval("1 = 1"), IsError("expression is not assignable")); + EXPECT_THAT(Eval("i = 1"), IsError("side effects are not supported")); + + EXPECT_THAT(Eval("p = 1"), + IsError("no known conversion from 'int' to 'float *'")); + EXPECT_THAT(Eval("eOne = 1"), + IsError("no known conversion from 'int' to 'Enum'")); + +#ifndef __EMSCRIPTEN__ + ASSERT_TRUE(CreateContextVariable("$i", "1")); + EXPECT_THAT(EvalWithContext("$i = 2", vars_), IsEqual("2")); + EXPECT_THAT(EvalWithContext("$i", vars_), IsEqual("2")); + EXPECT_THAT(EvalWithContext("$i = -2", vars_), IsEqual("-2")); + EXPECT_THAT(EvalWithContext("$i", vars_), IsEqual("-2")); + EXPECT_THAT(EvalWithContext("$i = eOne", vars_), IsEqual("0")); + EXPECT_THAT(EvalWithContext("$i", vars_), IsEqual("0")); + EXPECT_THAT(EvalWithContext("$i = eTwo", vars_), IsEqual("1")); + EXPECT_THAT(EvalWithContext("$i", vars_), IsEqual("1")); + + ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); + EXPECT_THAT(EvalWithContext("$f = 2.5", vars_), IsEqual("2.5")); + EXPECT_THAT(EvalWithContext("$f", vars_), IsEqual("2.5")); + EXPECT_THAT(EvalWithContext("$f = 3.5f", vars_), IsEqual("3.5")); + EXPECT_THAT(EvalWithContext("$f", vars_), IsEqual("3.5")); + + ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); + EXPECT_THAT(EvalWithContext("$s = 100000", vars_), IsEqual("-31072")); + EXPECT_THAT(EvalWithContext("$s", vars_), IsEqual("-31072")); + + ASSERT_TRUE(CreateContextVariable("$p", "(int*)10")); + EXPECT_THAT(EvalWithContext("$p = 1", vars_), + IsError("no known conversion from 'int' to 'int *'")); + EXPECT_THAT(EvalWithContext("$p = (int*)12", vars_), + IsEqual(Is32Bit() ? "0x0000000c" : "0x000000000000000c")); + EXPECT_THAT(EvalWithContext("$p", vars_), + IsEqual(Is32Bit() ? "0x0000000c" : "0x000000000000000c")); + EXPECT_THAT(EvalWithContext("$p = 0", vars_), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + EXPECT_THAT(EvalWithContext("$p = nullptr", vars_), + IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); + + ASSERT_TRUE(CreateContextVariableArray("int", "$arr", "{1, 2}")); + EXPECT_THAT(EvalWithContext("$p = $arr", vars_), IsOk()); + + ASSERT_TRUE(CreateContextVariableArray("float", "$farr", "{1.f, 2.f}")); + EXPECT_THAT(EvalWithContext("$p = $farr", vars_), + IsError("no known conversion from 'float[2]' to 'int *'")); +#endif +} + +TEST_F(EvalTest, DISABLED_TestCompositeAssignmentInvalid) { + EXPECT_THAT(Eval("1 += 1"), IsError("expression is not assignable")); + EXPECT_THAT(Eval("i += 1"), IsError("side effects are not supported")); + + EXPECT_THAT(Eval("1 -= 1"), IsError("expression is not assignable")); + EXPECT_THAT(Eval("i -= 1"), IsError("side effects are not supported")); + + EXPECT_THAT(Eval("1 *= 1"), IsError("expression is not assignable")); + EXPECT_THAT(Eval("i *= 1"), IsError("side effects are not supported")); + + EXPECT_THAT(Eval("1 /= 1"), IsError("expression is not assignable")); + EXPECT_THAT(Eval("i /= 1"), IsError("side effects are not supported")); + + EXPECT_THAT(Eval("1 %= 1"), IsError("expression is not assignable")); + EXPECT_THAT(Eval("i %= 1"), IsError("side effects are not supported")); + EXPECT_THAT( + Eval("f %= 1"), + IsError("invalid operands to binary expression ('float' and 'int')")); + +#ifndef __EMSCRIPTEN__ + ASSERT_TRUE(CreateContextVariable("Enum", "$e", false, "Enum::ONE")); + EXPECT_THAT( + EvalWithContext("$e *= 1", vars_), + // TODO(werat): This should actually be: + // > assigning to 'Enum' from incompatible type 'int' + IsError("invalid operands to binary expression ('Enum' and 'int')")); + + ASSERT_TRUE(CreateContextVariable("$i", "1")); + EXPECT_THAT(EvalWithContext("($i += 1) -= 2", vars_), + IsError("side effects are not supported in this context")); +#endif +} + +#ifndef __EMSCRIPTEN__ +TEST_F(EvalTest, DISABLED_TestCompositeAssignmentAdd) { + ASSERT_TRUE(CreateContextVariable("$i", "1")); + EXPECT_THAT(EvalWithContext("$i += 1", vars_), IsEqual("2")); + EXPECT_THAT(EvalWithContext("$i += 2", vars_), IsEqual("4")); + EXPECT_THAT(EvalWithContext("$i += -4", vars_), IsEqual("0")); + EXPECT_THAT(EvalWithContext("$i += eOne", vars_), IsEqual("0")); + EXPECT_THAT(EvalWithContext("$i += eTwo", vars_), IsEqual("1")); + + ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); + EXPECT_THAT(EvalWithContext("$f += 1", vars_), IsEqual("2.5")); + EXPECT_THAT(EvalWithContext("$f += -2", vars_), IsEqual("0.5")); + EXPECT_THAT(EvalWithContext("$f += 2.5", vars_), IsEqual("3")); + EXPECT_THAT(EvalWithContext("$f += eTwo", vars_), IsEqual("4")); + + ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); + EXPECT_THAT(EvalWithContext("$s += 1000", vars_), IsEqual("1100")); + EXPECT_THAT(EvalWithContext("$s += 100000", vars_), IsEqual("-29972")); + + ASSERT_TRUE(CreateContextVariable("$p", "(int*)10")); + EXPECT_THAT(EvalWithContext("$p += 1", vars_), + IsEqual(Is32Bit() ? "0x0000000e" : "0x000000000000000e")); + EXPECT_THAT( + EvalWithContext("$p += 1.5", vars_), + IsError("invalid operands to binary expression ('int *' and 'double')")); + EXPECT_THAT( + EvalWithContext("$p += $p", vars_), + IsError("invalid operands to binary expression ('int *' and 'int *')")); + EXPECT_THAT(EvalWithContext("$i += $p", vars_), + IsError("no known conversion from 'int *' to 'int'")); +} + +TEST_F(EvalTest, DISABLED_TestCompositeAssignmentSub) { + ASSERT_TRUE(CreateContextVariable("$i", "1")); + EXPECT_THAT(EvalWithContext("$i -= 1", vars_), IsEqual("0")); + EXPECT_THAT(EvalWithContext("$i -= 2", vars_), IsEqual("-2")); + EXPECT_THAT(EvalWithContext("$i -= -4", vars_), IsEqual("2")); + EXPECT_THAT(EvalWithContext("$i -= eOne", vars_), IsEqual("2")); + EXPECT_THAT(EvalWithContext("$i -= eTwo", vars_), IsEqual("1")); + + ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); + EXPECT_THAT(EvalWithContext("$f -= 1", vars_), IsEqual("0.5")); + EXPECT_THAT(EvalWithContext("$f -= -2", vars_), IsEqual("2.5")); + EXPECT_THAT(EvalWithContext("$f -= -2.5", vars_), IsEqual("5")); + EXPECT_THAT(EvalWithContext("$f -= eTwo", vars_), IsEqual("4")); + + ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); + EXPECT_THAT(EvalWithContext("$s -= 1000", vars_), IsEqual("-900")); + EXPECT_THAT(EvalWithContext("$s -= 100000", vars_), IsEqual("30172")); + + ASSERT_TRUE(CreateContextVariable("$p", "(int*)10")); + EXPECT_THAT(EvalWithContext("$p -= 1", vars_), + IsEqual(Is32Bit() ? "0x00000006" : "0x0000000000000006")); + +#ifdef _WIN32 + // On Windows, 'ptrdiff_t' is 'long long'. + EXPECT_THAT(EvalWithContext("$p -= $p", vars_), + IsError("no known conversion from 'long long' to 'int *'")); +#else + // On Linux, 'ptrdiff_t' is 'long'. + EXPECT_THAT(EvalWithContext("$p -= $p", vars_), + IsError("no known conversion from 'long' to 'int *'")); +#endif +} + +TEST_F(EvalTest, DISABLED_TestCompositeAssignmentMul) { + ASSERT_TRUE(CreateContextVariable("$i", "1")); + EXPECT_THAT(EvalWithContext("$i *= 1", vars_), IsEqual("1")); + EXPECT_THAT(EvalWithContext("$i *= 2", vars_), IsEqual("2")); + EXPECT_THAT(EvalWithContext("$i *= -2.5", vars_), IsEqual("-5")); + EXPECT_THAT(EvalWithContext("$i *= eTwo", vars_), IsEqual("-5")); + + ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); + EXPECT_THAT(EvalWithContext("$f *= 1", vars_), IsEqual("1.5")); + EXPECT_THAT(EvalWithContext("$f *= 2", vars_), IsEqual("3")); + EXPECT_THAT(EvalWithContext("$f *= -2.5", vars_), IsEqual("-7.5")); + EXPECT_THAT(EvalWithContext("$f *= eTwo", vars_), IsEqual("-7.5")); + + ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); + EXPECT_THAT(EvalWithContext("$s *= 1000", vars_), IsEqual("-31072")); + EXPECT_THAT(EvalWithContext("$s *= -1000", vars_), IsEqual("7936")); +} + +TEST_F(EvalTest, DISABLED_TestCompositeAssignmentDiv) { + ASSERT_TRUE(CreateContextVariable("$i", "15")); + EXPECT_THAT(EvalWithContext("$i /= 1", vars_), IsEqual("15")); + EXPECT_THAT(EvalWithContext("$i /= 2", vars_), IsEqual("7")); + EXPECT_THAT(EvalWithContext("$i /= -2", vars_), IsEqual("-3")); + EXPECT_THAT(EvalWithContext("$i /= eTwo", vars_), IsEqual("-3")); + + ASSERT_TRUE(CreateContextVariable("$f", "15.5f")); + EXPECT_THAT(EvalWithContext("$f /= 1", vars_), IsEqual("15.5")); + EXPECT_THAT(EvalWithContext("$f /= 2", vars_), IsEqual("7.75")); + EXPECT_THAT(EvalWithContext("$f /= -2.5", vars_), IsEqual("-3.0999999")); + EXPECT_THAT(EvalWithContext("$f /= eTwo", vars_), IsEqual("-3.0999999")); + + ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); + EXPECT_THAT(EvalWithContext("$s /= 10", vars_), IsEqual("10")); + EXPECT_THAT(EvalWithContext("$s /= -3", vars_), IsEqual("-3")); +} + +TEST_F(EvalTest, DISABLED_TestCompositeAssignmentRem) { + ASSERT_TRUE(CreateContextVariable("$i", "15")); + EXPECT_THAT(EvalWithContext("$i %= 8", vars_), IsEqual("7")); + EXPECT_THAT(EvalWithContext("$i %= -3", vars_), IsEqual("1")); + EXPECT_THAT(EvalWithContext("$i %= eTwo", vars_), IsEqual("0")); + + ASSERT_TRUE(CreateContextVariable("$f", "15.5f")); + EXPECT_THAT( + EvalWithContext("$f %= 1", vars_), + IsError("invalid operands to binary expression ('float' and 'int')")); + + ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); + EXPECT_THAT(EvalWithContext("$s %= 23", vars_), IsEqual("8")); +} + +TEST_F(EvalTest, DISABLED_TestCompositeAssignmentBitwise) { + ASSERT_TRUE(CreateContextVariable("$i", "0b11111111")); + EXPECT_THAT(EvalWithContext("$i &= 0b11110000", vars_), IsEqual("240")); + EXPECT_THAT(EvalWithContext("$i |= 0b01100011", vars_), IsEqual("243")); + EXPECT_THAT(EvalWithContext("$i ^= 0b00100010", vars_), IsEqual("209")); + EXPECT_THAT(EvalWithContext("$i <<= 2", vars_), IsEqual("836")); + EXPECT_THAT(EvalWithContext("$i >>= 3", vars_), IsEqual("104")); + EXPECT_THAT(EvalWithContext("$i <<= eTwo", vars_), IsEqual("208")); + EXPECT_THAT(EvalWithContext("$i >>= eTwo", vars_), IsEqual("104")); + EXPECT_THAT(EvalWithContext("$i <<= 1U", vars_), IsEqual("208")); + EXPECT_THAT(EvalWithContext("$i >>= 1LL", vars_), IsEqual("104")); + + ASSERT_TRUE(CreateContextVariable("$c", "(signed char)-1")); + EXPECT_THAT(EvalWithContext("$c &= 0b11110011", vars_), IsEqual("'\\xf3'")); + EXPECT_THAT(EvalWithContext("$c |= 0b01001011", vars_), IsEqual("'\\xfb'")); + EXPECT_THAT(EvalWithContext("$c ^= 0b00000110", vars_), IsEqual("'\\xfd'")); + EXPECT_THAT(EvalWithContext("$c <<= 5", vars_), IsEqual("'\\xa0'")); + EXPECT_THAT(EvalWithContext("$c >>= 20", vars_), IsEqual("'\\xff'")); + EXPECT_THAT(EvalWithContext("$c <<= 2U", vars_), IsEqual("'\\xfc'")); + EXPECT_THAT(EvalWithContext("$c >>= eTwo", vars_), IsEqual("'\\xfe'")); + + ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); + EXPECT_THAT( + EvalWithContext("$f &= 1", vars_), + IsError("invalid operands to binary expression ('float' and 'int')")); + EXPECT_THAT( + EvalWithContext("$f |= 1", vars_), + IsError("invalid operands to binary expression ('float' and 'int')")); + EXPECT_THAT( + EvalWithContext("$f ^= 1", vars_), + IsError("invalid operands to binary expression ('float' and 'int')")); + EXPECT_THAT( + EvalWithContext("$f <<= 1", vars_), + IsError("invalid operands to binary expression ('float' and 'int')")); + EXPECT_THAT( + EvalWithContext("$f >>= 1", vars_), + IsError("invalid operands to binary expression ('float' and 'int')")); + + ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); + EXPECT_THAT(EvalWithContext("$s >>= 2", vars_), IsEqual("25")); + EXPECT_THAT(EvalWithContext("$s <<= 6", vars_), IsEqual("1600")); + EXPECT_THAT(EvalWithContext("$s <<= 12", vars_), IsEqual("0")); + + ASSERT_TRUE(CreateContextVariable("$e", "eTwo")); + std::vector ops = {"&=", "|=", "^=", "<<=", ">>="}; + for (const auto &op : ops) { + const std::string expr = "$e " + op + " 1"; // e.g. "$e ^= 1" + EXPECT_THAT( + EvalWithContext(expr, vars_), + IsError("invalid operands to binary expression ('Enum' and 'int')")); + } + + ASSERT_TRUE(CreateContextVariable("$p", "(int*)10")); + EXPECT_THAT( + EvalWithContext("$p &= 1", vars_), + IsError("invalid operands to binary expression ('int *' and 'int')")); + EXPECT_THAT( + EvalWithContext("$p |= (char)1", vars_), + IsError("invalid operands to binary expression ('int *' and 'char')")); + EXPECT_THAT( + EvalWithContext("$p ^= &f", vars_), + IsError("invalid operands to binary expression ('int *' and 'float *')")); + EXPECT_THAT( + EvalWithContext("$p <<= 1", vars_), + IsError("invalid operands to binary expression ('int *' and 'int')")); + EXPECT_THAT( + EvalWithContext("$p >>= $p", vars_), + IsError("invalid operands to binary expression ('int *' and 'int *')")); +} + +TEST_F(EvalTest, DISABLED_TestSideEffects) { + // Comparing with LLDB is not possible with side effects enabled -- results + // will always be different (because the same expression is evaluated twice). + this->compare_with_lldb_ = false; + this->allow_side_effects_ = true; + + EXPECT_THAT(Eval("x++"), IsEqual("1")); + EXPECT_THAT(Eval("x"), IsEqual("2")); + EXPECT_THAT(Eval("++x"), IsEqual("3")); + + EXPECT_THAT(Eval("xa[0] = 4"), IsEqual("4")); + EXPECT_THAT(Eval("xa[0]"), IsEqual("4")); + EXPECT_THAT(Eval("xa[1] += xa[0]"), IsEqual("6")); + + EXPECT_THAT(Eval("*p = 5.2"), IsEqual("5")); + EXPECT_THAT(Eval("*p"), IsEqual("5")); + EXPECT_THAT(Eval("x"), IsEqual("5")); // `p` is `&x` +} +#endif + +TEST_F(EvalTest, TestBuiltinFunction_findnonnull) { + // LLDB doesn't support `__findnonnull` intrinsic function. + this->compare_with_lldb_ = false; + + EXPECT_THAT(Eval("__findnonnull(array_of_pointers, 0)"), IsEqual("-1")); + EXPECT_THAT(Eval("__findnonnull(array_of_pointers, 1)"), IsEqual("0")); + EXPECT_THAT(Eval("__findnonnull(array_of_pointers, 5)"), IsEqual("0")); + EXPECT_THAT(Eval("__findnonnull(array_of_pointers+2, 3)"), IsEqual("2")); + EXPECT_THAT(Eval("__findnonnull(array_of_pointers+3, 2)"), IsEqual("1")); + + EXPECT_THAT(Eval("__findnonnull(pointer_to_pointers, 0)"), IsEqual("-1")); + EXPECT_THAT(Eval("__findnonnull(pointer_to_pointers, 1)"), IsEqual("0")); + EXPECT_THAT(Eval("__findnonnull(pointer_to_pointers, 5)"), IsEqual("0")); + EXPECT_THAT(Eval("__findnonnull(pointer_to_pointers+2, 3)"), IsEqual("2")); + EXPECT_THAT(Eval("__findnonnull(pointer_to_pointers+3, 2)"), IsEqual("1")); + + EXPECT_THAT(Eval("__findnonnull(0, 0)"), + IsError("no known conversion from 'int' to 'T*' for 1st argument " + "of __findnonnull()\n" + "__findnonnull(0, 0)\n" + " ^")); + EXPECT_THAT( + Eval("__findnonnull(1.0f, 0)"), + IsError("no known conversion from 'float' to 'T*' for 1st argument " + "of __findnonnull()\n" + "__findnonnull(1.0f, 0)\n" + " ^")); + EXPECT_THAT( + Eval("__findnonnull(array_of_pointers, -1)"), + IsError("passing in a buffer size ('-1') that is negative or in excess " + "of 100 million to __findnonnull() is not allowed.\n" + "__findnonnull(array_of_pointers, -1)\n" + " ^")); + EXPECT_THAT( + Eval("__findnonnull(array_of_pointers, 100000001)"), + IsError("passing in a buffer size ('100000001') that is negative " + "or in excess of 100 million to __findnonnull() is not allowed.\n" + "__findnonnull(array_of_pointers, 100000001)\n" + " ^")); + + if (process_.GetAddressByteSize() == 4) { + EXPECT_THAT(Eval("__findnonnull(array_of_uint8, 6)"), IsEqual("0")); + EXPECT_THAT(Eval("__findnonnull(array_of_uint8+4, 5)"), IsEqual("0")); + EXPECT_THAT(Eval("__findnonnull(array_of_uint8+8, 4)"), IsEqual("2")); + EXPECT_THAT(Eval("__findnonnull(array_of_uint8+12, 3)"), IsEqual("1")); + } else if (process_.GetAddressByteSize() == 8) { + EXPECT_THAT(Eval("__findnonnull(array_of_uint8, 3)"), IsEqual("0")); + EXPECT_THAT(Eval("__findnonnull(array_of_uint8+8, 2)"), IsEqual("1")); + } else { + GTEST_FATAL_FAILURE_("Unknown pointer size in the target process"); + } +} + +TEST_F(EvalTest, TestUniquePtr) { +#ifdef _WIN32 + // On Windows we're not using `libc++` and therefore the layout of + // `std::unique_ptr` is different. + GTEST_SKIP() << "not supported on Windows"; +#else + // On Linux this assumes the usage of libc++ standard library. + + EXPECT_THAT(Eval("*(NodeU**)&ptr_node.__ptr_"), IsOk()); + EXPECT_THAT(Eval("(*(NodeU**)&ptr_node.__ptr_)->value"), IsEqual("1")); + + EXPECT_THAT(Eval("ptr_node.__ptr_"), IsOk()); + EXPECT_THAT(Eval("ptr_node.__ptr_->value"), IsEqual("1")); + EXPECT_THAT(Eval("ptr_node.__ptr_->next.__ptr_->value"), IsEqual("2")); +#endif +} + +TEST_F(EvalTest, TestUniquePtrDeref) { +#ifdef _WIN32 + // On Windows we're not using `libc++` and therefore the layout of + // `std::unique_ptr` is different. + GTEST_SKIP() << "not supported on Windows"; +#else + // On Linux this assumes the usage of libc++ standard library. + this->compare_with_lldb_ = false; + + // Test member-of dereference. + EXPECT_THAT(Eval("ptr_node->value"), IsEqual("1")); + EXPECT_THAT(Eval("ptr_node->next->value"), IsEqual("2")); + + // Test ptr dereference. + EXPECT_THAT(Eval("(*ptr_node).value"), IsEqual("1")); + EXPECT_THAT(Eval("(*(*ptr_node).next).value"), IsEqual("2")); +#endif +} + +TEST_F(EvalTest, TestUniquePtrCompare) { +#ifdef _WIN32 + // On Windows we're not using `libc++` and therefore the layout of + // `std::unique_ptr` is different. + GTEST_SKIP() << "not supported on Windows"; +#else + // On Linux this assumes the usage of libc++ standard library. + this->compare_with_lldb_ = false; + + EXPECT_THAT(Eval("ptr_int == nullptr"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_int != nullptr"), XFail(IsEqual("true"))); + EXPECT_THAT(Eval("ptr_int == ptr_int"), XFail(IsEqual("true"))); + + // C++ doesn't allow comparing unique_ptr with raw pointers, but we allow it + // for convenience. + EXPECT_THAT(Eval("ptr_int == 0"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_int == (int*)0"), XFail(IsEqual("false"))); + + EXPECT_THAT(Eval("ptr_float == nullptr"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_float != nullptr"), XFail(IsEqual("true"))); + EXPECT_THAT(Eval("ptr_float == (float*)0"), XFail(IsEqual("false"))); + + EXPECT_THAT(Eval("ptr_float == (int*)0"), + XFail(IsError("comparison of distinct pointer types"))); + EXPECT_THAT(Eval("ptr_int == ptr_float"), + XFail(IsError("comparison of distinct pointer types"))); + + EXPECT_THAT(Eval("ptr_null == nullptr"), XFail(IsEqual("true"))); + EXPECT_THAT(Eval("ptr_null != nullptr"), XFail(IsEqual("false"))); + + EXPECT_THAT(Eval("ptr_void == nullptr"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_void != nullptr"), XFail(IsEqual("true"))); + EXPECT_THAT(Eval("ptr_void == ptr_void"), XFail(IsEqual("true"))); + + // Void pointer can be compared with everything. + EXPECT_THAT(Eval("ptr_void == (int*)0"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_void == (void*)0"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_int == ptr_void"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_float == ptr_void"), XFail(IsEqual("false"))); + +#endif +} + +TEST_F(EvalTest, TestSharedPtr) { +#ifdef _WIN32 + // On Windows we're not using `libc++` and therefore the layout of + // `std::unique_ptr` is different. + GTEST_SKIP() << "not supported on Windows"; +#else + // On Linux this assumes the usage of libc++ standard library. + + EXPECT_THAT(Eval("*(NodeS**)&ptr_node.__ptr_"), IsOk()); + EXPECT_THAT(Eval("(*(NodeS**)&ptr_node.__ptr_)->value"), IsEqual("1")); + + EXPECT_THAT(Eval("ptr_node.__ptr_"), IsOk()); + EXPECT_THAT(Eval("ptr_node.__ptr_->value"), IsEqual("1")); + EXPECT_THAT(Eval("ptr_node.__ptr_->next.__ptr_->value"), IsEqual("2")); + + EXPECT_THAT(Eval("ptr_int.__ptr_"), IsOk()); + EXPECT_THAT(Eval("*ptr_int.__ptr_"), IsEqual("1")); + EXPECT_THAT(Eval("ptr_int_weak.__ptr_"), IsOk()); + EXPECT_THAT(Eval("*ptr_int_weak.__ptr_"), IsEqual("1")); +#endif +} + +TEST_F(EvalTest, TestSharedPtrDeref) { +#ifdef _WIN32 + // On Windows we're not using `libc++` and therefore the layout of + // `std::shared_ptr` is different. + GTEST_SKIP() << "not supported on Windows"; +#else + // On Linux this assumes the usage of libc++ standard library. + this->compare_with_lldb_ = false; + + // Test member-of dereference. + EXPECT_THAT(Eval("ptr_node->value"), IsEqual("1")); + EXPECT_THAT(Eval("ptr_node->next->value"), IsEqual("2")); + + // Test ptr dereference. + EXPECT_THAT(Eval("(*ptr_node).value"), IsEqual("1")); + EXPECT_THAT(Eval("(*(*ptr_node).next).value"), IsEqual("2")); +#endif +} + +TEST_F(EvalTest, TestSharedPtrCompare) { +#ifdef _WIN32 + // On Windows we're not using `libc++` and therefore the layout of + // `std::shared_ptr` is different. + GTEST_SKIP() << "not supported on Windows"; +#else + // On Linux this assumes the usage of libc++ standard library. + this->compare_with_lldb_ = false; + + EXPECT_THAT(Eval("ptr_int == nullptr"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_int != nullptr"), XFail(IsEqual("true"))); + EXPECT_THAT(Eval("ptr_int == ptr_int"), XFail(IsEqual("true"))); + + // C++ doesn't allow comparing shared_ptr with raw pointers, but we allow it + // for convenience. + EXPECT_THAT(Eval("ptr_int == 0"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_int == (int*)0"), XFail(IsEqual("false"))); + + EXPECT_THAT(Eval("ptr_float == nullptr"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_float != nullptr"), XFail(IsEqual("true"))); + EXPECT_THAT(Eval("ptr_float == (float*)0"), XFail(IsEqual("false"))); + + EXPECT_THAT(Eval("ptr_float == (int*)0"), + XFail(IsError("comparison of distinct pointer types"))); + EXPECT_THAT(Eval("ptr_int == ptr_float"), + XFail(IsError("comparison of distinct pointer types"))); + + EXPECT_THAT(Eval("ptr_null == nullptr"), XFail(IsEqual("true"))); + EXPECT_THAT(Eval("ptr_null != nullptr"), XFail(IsEqual("false"))); + + EXPECT_THAT(Eval("ptr_void == nullptr"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_void != nullptr"), XFail(IsEqual("true"))); + EXPECT_THAT(Eval("ptr_void == ptr_void"), XFail(IsEqual("true"))); + + // Void pointer can be compared with everything. + EXPECT_THAT(Eval("ptr_void == (int*)0"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_void == (void*)0"), XFail(IsEqual("false"))); + EXPECT_THAT(Eval("ptr_int == ptr_void"), XFail(IsEqual("true"))); + EXPECT_THAT(Eval("ptr_float == ptr_void"), XFail(IsEqual("false"))); +#endif +} + +TEST_F(EvalTest, TestTypeComparison) { + // This test is for border-case situations in the CompareTypes function. + + // Taking an address of ternary expression require operands of the same type. + EXPECT_THAT(Eval("&(true ? ip : icpc)"), IsOk()); + EXPECT_THAT(Eval("&(true ? mipp : ipp)"), + XFail(IsOk(/*compare_types*/ false))); + EXPECT_THAT(Eval("&(true ? ipp : icpcpc)"), IsOk()); + EXPECT_THAT(Eval("&(true ? ipp : mipp)"), XFail(IsOk())); + EXPECT_THAT(Eval("&(true ? ipp : micpcpc)"), IsOk()); + // TODO: Enable type comparison once the type mismatch is fixed. + // LLDB results in "int ***", while lldb-eval results in "MyInt ***". + EXPECT_THAT(Eval("&(true ? mipp : icpcpc)"), + XFail(IsOk(/*compare_types*/ false))); + EXPECT_THAT(Eval("&(true ? mipp : micpcpc)"), + XFail(IsOk(/*compare_types*/ false))); + EXPECT_THAT(Eval("&(true ? icpcpc : micpcpc)"), IsOk()); + + // Ensure that "signed char" and "char" are different types. + EXPECT_THAT(Eval("true ? c : sc"), IsEqual("2")); // int + EXPECT_THAT(Eval("true ? sc : (signed char)67"), IsEqual("'A'")); + EXPECT_THAT(Eval("true ? (char)66 : (signed char)65"), IsEqual("66")); + EXPECT_THAT(Eval("true ? cc : mc"), XFail(IsEqual("'B'"))); + EXPECT_THAT(Eval("true ? cc : sc"), IsEqual("66")); + EXPECT_THAT(Eval("true ? sc : mc"), IsEqual("65")); + EXPECT_THAT(Eval("&(true ? c : c)"), IsOk()); + EXPECT_THAT(Eval("&(true ? c : sc)"), + IsError("cannot take the address of an rvalue of type 'int'")); +} + +TEST_F(EvalTest, TestTypeVsIdentifier) { + EXPECT_THAT(Eval("(StructOrVar)+1"), IsEqual("3")); + EXPECT_THAT(Eval("(StructOrVar)1"), + IsError("expected 'eof', got: <'1' (numeric_constant)>")); + EXPECT_THAT(Eval("(ClassOrVar.x - 1)"), IsEqual("2")); + EXPECT_THAT(Eval("(ClassOrVar).x - 1"), IsEqual("2")); + EXPECT_THAT( + Eval("(ClassOrVar)+1"), + IsError( + "invalid operands to binary expression ('ClassOrVar' and 'int')")); + EXPECT_THAT(Eval("(UnionOrVar)[1]"), IsEqual("2")); + EXPECT_THAT(Eval("*(UnionOrVar)"), IsEqual("1")); + EXPECT_THAT(Eval("(*UnionOrVar)"), IsEqual("1")); + + EXPECT_THAT(Eval("sizeof(StructOrVar)"), IsEqual("2")); + + EXPECT_THAT(Eval("static_cast(0)"), + IsError("unknown type name 'OnlyVar'")); + EXPECT_THAT(Eval("static_cast(0)"), + IsError("must use 'struct' tag to refer to type 'StructOrVar' in " + "this scope")); + EXPECT_THAT( + Eval("static_cast(0)"), + IsError( + "must use 'class' tag to refer to type 'ClassOrVar' in this scope")); + EXPECT_THAT( + Eval("static_cast(0)"), + IsError( + "must use 'union' tag to refer to type 'UnionOrVar' in this scope")); + EXPECT_THAT( + Eval("static_cast(0)"), + IsError( + "must use 'enum' tag to refer to type 'EnumOrVar' in this scope")); + EXPECT_THAT( + Eval("static_cast(0)"), + IsError( + "must use 'enum' tag to refer to type 'CxxEnumOrVar' in this scope")); +} + +#ifndef __EMSCRIPTEN__ +TEST_F(EvalTest, DISABLED_TestSeparateParsing) { + lldb::SBError error; + + auto expr_a = Scope("a").Compile("a_", error); + ASSERT_TRUE(error.Success()); + + auto expr_b = Scope("b").Compile("b_", error); + ASSERT_TRUE(error.Success()); + + auto expr_c = Scope("c").Compile("a_ * b_ * c_", error); + ASSERT_TRUE(error.Success()); + + auto expr_d = Scope("d").Compile("a_ * b_ * c_ * d_", error); + ASSERT_TRUE(error.Success()); + + Scope("c").Compile("this", error); + ASSERT_TRUE(error.Success()); + + EXPECT_THAT(Scope("a").Eval(expr_a), IsEqual("1")); + EXPECT_THAT(Scope("b").Eval(expr_b), IsEqual("2")); + EXPECT_THAT(Scope("c").Eval(expr_c), IsEqual("60")); + EXPECT_THAT(Scope("d").Eval(expr_d), IsEqual("3024")); + + EXPECT_THAT(Scope("c").Eval(expr_a), IsEqual("3")); + EXPECT_THAT(Scope("c").Eval(expr_b), IsEqual("4")); + EXPECT_THAT(Scope("d").Eval(expr_a), IsEqual("6")); + EXPECT_THAT(Scope("d").Eval(expr_b), IsEqual("7")); + EXPECT_THAT(Scope("d").Eval(expr_c), IsEqual("336")); + + // Expression parsed in derived-type scope, evaluated in base-type scope. + EXPECT_THAT( + Scope("c").Eval(expr_d), + IsError("expression isn't parsed in the context of compatible type")); +} + +TEST_F(EvalTest, DISABLED_TestSeparateParsingWithContextVars) { + ASSERT_TRUE(CreateContextVariable("$x", "1")); + ASSERT_TRUE(CreateContextVariable("$y", "2.5")); + + std::unordered_map args = { + {"$x", vars_["$x"].GetType()}, {"$y", vars_["$y"].GetType()}}; + + lldb::SBError error; + + auto expr_c = Scope("c").CompileWithContext("c_ + $x + $y", args, error); + ASSERT_TRUE(error.Success()); + + EXPECT_THAT(Scope("c").EvalWithContext(expr_c, vars_), IsEqual("8.5")); + EXPECT_THAT(Scope("d").EvalWithContext(expr_c, vars_), IsEqual("11.5")); + + // Parsed context arguments don't match variables' types in evaluation. + std::unordered_map wrong_vars = { + {"$x", vars_["$y"]}, {"$y", vars_["$x"]}}; + EXPECT_THAT(Scope("c").EvalWithContext(expr_c, wrong_vars), + IsError("unexpected type of context variable '$x' (expected " + "'int', got 'double')")); + + // Context variable missing for parsed argument. + std::unordered_map incomplete_vars = { + {"$x", vars_["$x"]}}; + EXPECT_THAT(Scope("c").EvalWithContext(expr_c, incomplete_vars), + IsError("use of undeclared identifier '$y'")); +} + +TEST_F(EvalTest, DISABLED_TestRegisters) { + // LLDB loses the value formatter when evaluating registers and prints their + // value "as is". In lldb-eval the value formatter is preserved and the + // register can be "pretty-printed" depending on its type (e.g. vector + // registers are printed as vectors). + // Add an explicit cast so that the values are the same and can be compared. + EXPECT_THAT(Eval("(uint64_t) $rax"), IsOk()); + EXPECT_THAT(Eval("(uint64_t) $rbx"), IsOk()); + EXPECT_THAT(Eval("(uint64_t) $rcx"), IsOk()); + EXPECT_THAT(Eval("(uint64_t) $rdx"), IsOk()); + EXPECT_THAT(Eval("(uint64_t) $rdi"), IsOk()); + EXPECT_THAT(Eval("(uint64_t) $rsi"), IsOk()); + EXPECT_THAT(Eval("(uint64_t) $rbp"), IsOk()); + EXPECT_THAT(Eval("(uint64_t) $rsp"), IsOk()); + EXPECT_THAT(Eval("(uint64_t) $flags"), IsOk()); +} + +TEST_F(EvalTest, TestRegistersNoDollar) { + this->compare_with_lldb_ = false; + + // lldb-eval also supports accessing registers without `$`. Any other + // identifier (e.g. local variable) will shadow the register. + + // Shadowed by a local variable. + EXPECT_THAT(Eval("rax"), IsEqual("42")); + // Shadowed by a member field. + EXPECT_THAT(Eval("rbx"), IsEqual("42")); + // Shadowed by a global field. + EXPECT_THAT(Eval("rcx"), IsEqual("42")); + + // Not shadowed by anything. + for (std::string reg_name : {"rdx", "rdi", "rsi", "rbp", "rsp", "flags"}) { + lldb::SBValue reg = frame_.FindRegister(reg_name.c_str()); + EXPECT_THAT(Eval(reg_name), IsEqual(reg.GetValue())); + } +} +#endif + +TEST_F(EvalTest, DISABLED_TestCharParsing) { + EXPECT_THAT(Eval("1 + 'A'"), IsEqual("66")); + EXPECT_THAT(Eval("'B' - 'A'"), IsEqual("1")); + EXPECT_THAT(Eval("'A' == 'B'"), IsEqual("false")); + EXPECT_THAT(Eval("'A' == 'A'"), IsEqual("true")); + + // Following test fails detecting different results from lldb + // as u8 prefix is not handled in lldb + // EXPECT_THAT(Eval("1 + u8'A'"), IsEqual("66")); + // EXPECT_THAT(Eval("u8'B' - u8'A'"), IsEqual("1")); + // EXPECT_THAT(Eval("u8'A' == u8'B'"), IsEqual("false")); + // EXPECT_THAT(Eval("u8'A' == u8'A'"), IsEqual("true")); + + EXPECT_THAT(Eval("1 + u'A'"), IsEqual("66")); + EXPECT_THAT(Eval("u'B' - u'A'"), IsEqual("1")); + EXPECT_THAT(Eval("u'A' == u'B'"), IsEqual("false")); + EXPECT_THAT(Eval("u'A' == u'A'"), IsEqual("true")); + + EXPECT_THAT(Eval("1 + U'A'"), IsEqual("66")); + EXPECT_THAT(Eval("U'B' - U'A'"), IsEqual("1")); + EXPECT_THAT(Eval("U'A' == U'B'"), IsEqual("false")); + EXPECT_THAT(Eval("U'A' == U'A'"), IsEqual("true")); + + EXPECT_THAT(Eval("1 + L'A'"), IsEqual("66")); + EXPECT_THAT(Eval("L'B' - L'A'"), IsEqual("1")); + EXPECT_THAT(Eval("L'A' == L'B'"), IsEqual("false")); + EXPECT_THAT(Eval("L'A' == L'A'"), IsEqual("true")); + + EXPECT_THAT(Eval("1 + 'ABC'"), IsEqual("4276804")); + EXPECT_THAT(Eval("'ABD' - 'ABC'"), IsEqual("1")); + EXPECT_THAT(Eval("'ABC' == 'BCD'"), IsEqual("false")); + EXPECT_THAT(Eval("'ABC' == 'ABC'"), IsEqual("true")); + + EXPECT_THAT(Eval("U'ъ' == U'猫'"), IsEqual("false")); + EXPECT_THAT(Eval("U'ü' == U'ü'"), IsEqual("true")); + EXPECT_THAT(Eval("U'u' == U'ü'"), IsEqual("false")); + + // Multichar contants with more than 4 chars generate + // compiler warning in clang + // Only the rightmost 4 characters are considered + // to compute the constant's value + EXPECT_THAT(Eval("'Xabcd' == 'Yabcd'"), IsEqual("true")); + EXPECT_THAT(Eval("'abcdX' == 'abcdY'"), IsEqual("false")); +} + +#ifndef __EMSCRIPTEN__ +TEST_F(EvalTest, DISABLED_TestStringParsing) { + lldb::SBError ignore; + // Comparing is done manually (instead of using IsOk and IsEqual matchers). + // This is because `SBValue::GetValue()` returns a nullptr for array types. + { + auto result = Eval("\"abc\""); + EXPECT_TRUE(result.lldb_DIL_error.Success()); + EXPECT_TRUE(result.lldb_DIL_value.IsValid()); + auto data = result.lldb_DIL_value.GetData(); + EXPECT_EQ(data.GetByteSize(), (size_t)4); + EXPECT_STREQ(data.GetString(ignore, 0), "abc"); + } + { + auto result = Eval("\"\""); + EXPECT_TRUE(result.lldb_DIL_error.Success()); + EXPECT_TRUE(result.lldb_DIL_value.IsValid()); + auto data = result.lldb_DIL_value.GetData(); + EXPECT_EQ(data.GetByteSize(), (size_t)1); + EXPECT_STREQ(data.GetString(ignore, 0), ""); + } + { + auto result = Eval("u8\"abc\""); + EXPECT_TRUE(result.lldb_DIL_value.IsValid()); + auto data = result.lldb_DIL_value.GetData(); + EXPECT_EQ(data.GetByteSize(), (size_t)4); + EXPECT_STREQ(data.GetString(ignore, 0), "abc"); + } + { + auto result = Eval("u\"abc\""); + EXPECT_TRUE(result.lldb_DIL_value.IsValid()); + auto data = result.lldb_DIL_value.GetData(); + EXPECT_EQ(data.GetByteSize(), (size_t)8); + char16_t val[4]; + EXPECT_EQ(data.ReadRawData(ignore, 0, reinterpret_cast(val), 8), + (size_t)8); + EXPECT_EQ(val[0], u'a'); + EXPECT_EQ(val[1], u'b'); + EXPECT_EQ(val[2], u'c'); + EXPECT_EQ(val[3], 0); + } + { + auto result = Eval("U\"猫ъü\""); + EXPECT_TRUE(result.lldb_DIL_value.IsValid()); + auto data = result.lldb_DIL_value.GetData(); + EXPECT_EQ(data.GetByteSize(), (size_t)16); + char32_t val[4]; + EXPECT_EQ( + data.ReadRawData(ignore, 0, reinterpret_cast(val), (size_t)16), + (size_t)16); + EXPECT_EQ(val[0], U'猫'); + EXPECT_EQ(val[1], U'ъ'); + EXPECT_EQ(val[2], U'ü'); + EXPECT_EQ(val[3], U'\0'); + } + { + auto result = Eval("L\"abc\""); + EXPECT_TRUE(result.lldb_DIL_value.IsValid()); + auto data = result.lldb_DIL_value.GetData(); + wchar_t val[4]; +#ifdef _WIN32 + // On Windows it holds sizeof(wchar_t) == 2. + EXPECT_EQ(data.GetByteSize(), (size_t)(size_t)8); + EXPECT_EQ(data.ReadRawData(ignore, 0, reinterpret_cast(val), 8), + (size_t)8); +#else + EXPECT_EQ(data.GetByteSize(), (size_t)16); + EXPECT_EQ(data.ReadRawData(ignore, 0, reinterpret_cast(val), 16), + (size_t)16); +#endif + EXPECT_EQ(val[0], L'a'); + EXPECT_EQ(val[1], L'b'); + EXPECT_EQ(val[2], L'c'); + EXPECT_EQ(val[3], 0); + } + + // TODO: lldb-eval should be able to handle these cases once string-literals + // are properly supported. + EXPECT_THAT(Eval("\"abc\" \"def\""), + IsError("string literals are not supported")); + EXPECT_THAT(Eval("\"abc\" + 1"), + IsError("string literals are not supported")); + EXPECT_THAT(Eval("*\"abc\""), IsError("string literals are not supported")); +} +#endif diff --git a/lldb/unittests/DIL/Inputs/CMakeLists.txt b/lldb/unittests/DIL/Inputs/CMakeLists.txt new file mode 100644 index 000000000000..a417a8a2631f --- /dev/null +++ b/lldb/unittests/DIL/Inputs/CMakeLists.txt @@ -0,0 +1,32 @@ +# Build `test_binary.cc` and put the binary in the Inputs folder, +# allowing `lldb_private::GetInputFilePath` to find it. +# Projects that must be enabled: clang;lldb;lld +# Runtimes that must be enabled: libcxx;libcxxabi;libunwind +if ("libcxx" IN_LIST LLVM_ENABLE_RUNTIMES) + if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE) + set(LIBCXX_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${LLVM_DEFAULT_TARGET_TRIPLE}) + set(LIBCXX_GENERATED_INCLUDE_DIR "${LLVM_BINARY_DIR}/include/c++/v1") + set(LIBCXX_GENERATED_INCLUDE_TARGET_DIR "${LLVM_BINARY_DIR}/include/${LLVM_DEFAULT_TARGET_TRIPLE}/c++/v1") + else() + set(LIBCXX_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBCXX_LIBDIR_SUFFIX}) + set(LIBCXX_GENERATED_INCLUDE_DIR "${CMAKE_BINARY_DIR}/include/c++/v1") + set(LIBCXX_GENERATED_INCLUDE_TARGET_DIR "${CMAKE_BINARY_DIR}/include/c++/v1") + endif() + + get_target_property(EXE_PATH DILTests RUNTIME_OUTPUT_DIRECTORY) + add_custom_command( + OUTPUT test_binary.bin + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/test_binary.cc + -O0 -g -std=c++17 -fuse-ld=lld -B$ + -L ${LIBCXX_LIBRARY_DIR} -stdlib=libc++ -lc++ + -I ${LIBCXX_GENERATED_INCLUDE_DIR} + -I ${LIBCXX_GENERATED_INCLUDE_TARGET_DIR} + -o ${EXE_PATH}/Inputs/test_binary.bin + DEPENDS test_binary.cc clang lld + ) + add_custom_target(test_binary + DEPENDS test_binary.bin + ) +else() + message(FATAL_ERROR "libcxx runtime must be enabled.") +endif() diff --git a/lldb/unittests/DIL/Inputs/test_binary.cc b/lldb/unittests/DIL/Inputs/test_binary.cc new file mode 100644 index 000000000000..f1f77a8398a7 --- /dev/null +++ b/lldb/unittests/DIL/Inputs/test_binary.cc @@ -0,0 +1,1251 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +static void TestArithmetic() { + char c = 10; + unsigned char uc = 1; + int a = 1; + int int_max = std::numeric_limits::max(); + int int_min = std::numeric_limits::min(); + unsigned int uint_max = std::numeric_limits::max(); + unsigned int uint_zero = 0; + long long ll_max = std::numeric_limits::max(); + long long ll_min = std::numeric_limits::min(); + unsigned long long ull_max = std::numeric_limits::max(); + unsigned long long ull_zero = 0; + + int x = 2; + int &r = x; + int *p = &x; + + typedef int &myr; + myr my_r = x; + + auto fnan = std::numeric_limits::quiet_NaN(); + auto fsnan = std::numeric_limits::signaling_NaN(); + // Smallest positive non-zero float denormal + auto fdenorm = 0x0.1p-145f; + + // BREAK(TestArithmetic) + // BREAK(TestZeroDivision) +} + +static void TestBitwiseOperators() { + bool var_true = true; + bool var_false = false; + + unsigned long long ull_max = std::numeric_limits::max(); + unsigned long long ull_zero = 0; + + struct S { + } s; + + const char *p = nullptr; + + uint32_t mask_ff = 0xFF; + + // BREAK(TestBitwiseOperators) +} + +static void TestPointerArithmetic() { + int *p_null = nullptr; + const char *p_char1 = "hello"; + + typedef const char *my_char_ptr; + my_char_ptr my_p_char1 = p_char1; + + int offset = 5; + int array[10]; + array[0] = 0; + array[offset] = offset; + + int(&array_ref)[10] = array; + + int *p_int0 = &array[0]; + int **pp_int0 = &p_int0; + const int *cp_int0 = &array[0]; + const int *cp_int5 = &array[offset]; + + typedef int *td_int_ptr_t; + td_int_ptr_t td_int_ptr0 = &array[0]; + + void *p_void = (void *)p_char1; + void **pp_void0 = &p_void; + void **pp_void1 = pp_void0 + 1; + + std::nullptr_t std_nullptr_t = nullptr; + + // BREAK(TestPointerArithmetic) + // BREAK(PointerPointerArithmeticFloat) + // BREAK(PointerPointerComparison) + // BREAK(PointerIntegerComparison) + // BREAK(TestPointerDereference) +} + +static void TestLogicalOperators() { + bool trueVar = true; + bool falseVar = false; + + const char *p_ptr = "🦊"; + const char *p_nullptr = nullptr; + + int array[2] = {1, 2}; + + struct S { + } s; + + // BREAK(TestLogicalOperators) +} + +static void TestLocalVariables() { + int a = 1; + int b = 2; + + char c = -3; + unsigned short s = 4; + + // BREAK(TestLocalVariables) +} + +static void TestMemberOf() { + int x = 2; + struct Sx { + int x; + int &r; + char y; + } s{1, x, 2}; + + Sx &sr = s; + Sx *sp = &s; + + Sx sarr[2] = {{5, x, 2}, {1, x, 3}}; + + using SxAlias = Sx; + SxAlias sa{3, x, 4}; + + // BREAK(TestMemberOf) +} + +static void TestMemberOfInheritance() { + struct A { + int a_; + } a{1}; + + struct B { + int b_; + } b{2}; + + struct C : A, B { + int c_; + } c{{1}, {2}, 3}; + + struct D : C { + int d_; + A fa_; + } d{{{1}, {2}, 3}, 4, {5}}; + + // Virtual inheritance example. + struct Animal { + virtual ~Animal() = default; + int weight_; + }; + struct Mammal : virtual Animal {}; + struct WingedAnimal : virtual Animal {}; + struct Bat : Mammal, WingedAnimal { + } bat; + bat.weight_ = 10; + + // Empty bases example. + struct IPlugin { + virtual ~IPlugin() {} + }; + struct Plugin : public IPlugin { + int x; + int y; + }; + Plugin plugin; + plugin.x = 1; + plugin.y = 2; + + struct ObjectBase { + int x; + }; + struct Object : ObjectBase {}; + struct Engine : Object { + int y; + int z; + }; + + Engine engine; + engine.x = 1; + engine.y = 2; + engine.z = 3; + + // Empty multiple inheritance with empty base. + struct Base { + int x; + int y; + virtual void Do() = 0; + virtual ~Base() {} + }; + struct Mixin {}; + struct Parent : private Mixin, public Base { + int z; + virtual void Do() {}; + }; + Parent obj; + obj.x = 1; + obj.y = 2; + obj.z = 3; + Base *parent_base = &obj; + Parent *parent = &obj; + + // BREAK(TestMemberOfInheritance) +} + +static void TestMemberOfAnonymousMember() { + struct A { + struct { + int x = 1; + }; + int y = 2; + } a; + + struct B { + // Anonymous struct inherits another struct. + struct : public A { + int z = 3; + }; + int w = 4; + A a; + } b; + + // Anonymous classes and unions. + struct C { + union { + int x = 5; + }; + class { + public: + int y = 6; + }; + } c; + + // Multiple levels of anonymous structs. + struct D { + struct { + struct { + int x = 7; + struct { + int y = 8; + }; + }; + int z = 9; + struct { + int w = 10; + }; + }; + } d; + + struct E { + struct IsNotAnon { + int x = 11; + }; + } e; + + struct F { + struct { + int x = 12; + } named_field; + } f; + + // Inherited unnamed struct without an enclosing parent class. + struct : public A { + struct { + int z = 13; + }; + } unnamed_derived; + + struct DerivedB : public B { + struct { + // `w` in anonymous struct overrides `w` from `B`. + int w = 14; + int k = 15; + }; + } derb; + + // BREAK(TestMemberOfAnonymousMember) +} + +static void TestIndirection() { + int val = 1; + int *p = &val; + + typedef int *myp; + myp my_p = &val; + + typedef int *&mypr; + mypr my_pr = p; + + // BREAK(TestIndirection) +} + +// Referenced by TestInstanceVariables +class C { +public: + int field_ = 1337; +}; + +// Referenced by TestAddressOf +int globalVar = 0xDEADBEEF; +extern int externGlobalVar; + +int *globalPtr = &globalVar; +int &globalRef = globalVar; + +namespace ns { +int globalVar = 13; +int *globalPtr = &globalVar; +int &globalRef = globalVar; +} // namespace ns + +void TestGlobalVariableLookup() { + // BREAK(TestGlobalVariableLookup) +} + +class TestMethods { +public: + void TestInstanceVariables() { + C c; + c.field_ = -1; + + C &c_ref = c; + C *c_ptr = &c; + + // BREAK(TestInstanceVariables) + } + + void TestAddressOf(int param) { + int x = 42; + int &r = x; + int *p = &x; + int *&pr = p; + + typedef int *&mypr; + mypr my_pr = p; + + std::string s = "hello"; + const char *s_str = s.c_str(); + + char c = 1; + + // BREAK(TestAddressOf) + } + +private: + int field_ = 1; +}; + +static void TestSubscript() { + const char *char_ptr = "lorem"; + const char char_arr[] = "ipsum"; + + int int_arr[] = {1, 2, 3}; + + C c_arr[2]; + c_arr[0].field_ = 0; + c_arr[1].field_ = 1; + + C(&c_arr_ref)[2] = c_arr; + + int idx_1 = 1; + const int &idx_1_ref = idx_1; + + typedef int td_int_t; + typedef td_int_t td_td_int_t; + typedef int *td_int_ptr_t; + typedef int &td_int_ref_t; + + td_int_t td_int_idx_1 = 1; + td_td_int_t td_td_int_idx_2 = 2; + + td_int_t td_int_arr[3] = {1, 2, 3}; + td_int_ptr_t td_int_ptr = td_int_arr; + + td_int_ref_t td_int_idx_1_ref = td_int_idx_1; + td_int_t(&td_int_arr_ref)[3] = td_int_arr; + + unsigned char uchar_idx = std::numeric_limits::max(); + uint8_t uint8_arr[256]; + uint8_arr[255] = 0xAB; + uint8_t *uint8_ptr = uint8_arr; + + enum Enum { kZero, kOne } enum_one = kOne; + Enum &enum_ref = enum_one; + + // BREAK(TestSubscript) +} + +static void TestArrayDereference() { + int arr_1d[2] = {1, 2}; + int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}}; + + // BREAK(TestArrayDereference) +} + +// Referenced by TestCStyleCast +namespace ns { + +typedef int myint; + +class Foo {}; + +namespace inner { + +using mydouble = double; + +class Foo {}; + +} // namespace inner + +} // namespace ns + +static void TestCStyleCast() { + int a = 1; + int *ap = &a; + void *vp = &a; + int arr[2] = {1, 2}; + + int na = -1; + float f = 1.1; + + typedef int myint; + + myint myint_ = 1; + ns::myint ns_myint_ = 2; + ns::Foo ns_foo_; + ns::Foo *ns_foo_ptr_ = &ns_foo_; + + ns::inner::mydouble ns_inner_mydouble_ = 1.2; + ns::inner::Foo ns_inner_foo_; + ns::inner::Foo *ns_inner_foo_ptr_ = &ns_inner_foo_; + + float finf = std::numeric_limits::infinity(); + float fnan = std::numeric_limits::quiet_NaN(); + float fsnan = std::numeric_limits::signaling_NaN(); + float fmax = std::numeric_limits::max(); + float fdenorm = std::numeric_limits::denorm_min(); + + // BREAK(TestCStyleCastBuiltins) + // BREAK(TestCStyleCastBasicType) + // BREAK(TestCStyleCastPointer) + // BREAK(TestCStyleCastNullptrType) + + struct InnerFoo { + int a; + int b; + }; + + InnerFoo ifoo; + (void)ifoo; + + int arr_1d[] = {1, 2, 3, 4}; + int arr_2d[2][3] = {{1, 2, 3}, {4, 5, 6}}; + + // BREAK(TestCStyleCastArray) + // BREAK(TestCStyleCastReference) +} + +// Referenced by TestCxxCast +struct CxxVirtualBase { + int a; + virtual ~CxxVirtualBase(){}; +}; +struct CxxVirtualParent : CxxVirtualBase { + int b; +}; + +static void TestCxxCast() { + struct CxxBase { + int a; + int b; + }; + struct CxxParent : CxxBase { + long long c; + short d; + }; + + enum UEnum { kUZero, kUOne, kUTwo }; + enum class SEnum { kSZero, kSOne }; + + UEnum u_enum = kUTwo; + SEnum s_enum = SEnum::kSOne; + + typedef int td_int_t; + typedef int *td_int_ptr_t; + typedef int &td_int_ref_t; + typedef SEnum td_senum_t; + td_int_t td_int = 13; + td_int_ptr_t td_int_ptr = &td_int; + td_int_ref_t td_int_ref = td_int; + td_senum_t td_senum = s_enum; + + CxxParent parent; + parent.a = 1; + parent.b = 2; + parent.c = 3; + parent.d = 4; + + CxxBase *base = &parent; + + int arr[] = {1, 2, 3, 4, 5}; + int *ptr = arr; + + // BREAK(TestCxxStaticCast) + // BREAK(TestCxxReinterpretCast) + + CxxVirtualParent v_parent; + v_parent.a = 1; + v_parent.b = 2; + CxxVirtualBase *v_base = &v_parent; + + // BREAK(TestCxxDynamicCast) +} + +void TestCastInheritedTypes() { + struct CxxEmpty {}; + struct CxxA { + short a; + }; + struct CxxB { + long long b; + }; + struct CxxC : CxxEmpty, CxxA, CxxB { + int c; + }; + struct CxxD { + long long d; + }; + struct CxxE : CxxD, CxxC { + int e; + }; + + CxxA a{1}; + CxxB b{2}; + CxxC c; + c.a = 3; + c.b = 4; + c.c = 5; + CxxD d{6}; + CxxE e; + e.a = 7; + e.b = 8; + e.c = 9; + e.d = 10; + e.e = 11; + + struct CxxVC : virtual CxxA, virtual CxxB { + int c; + }; + struct CxxVE : CxxD, CxxVC { + int e; + }; + + CxxVC vc; + vc.a = 12; + vc.b = 13; + vc.c = 14; + CxxVE ve; + ve.a = 15; + ve.b = 16; + ve.c = 17; + ve.d = 18; + ve.e = 19; + + CxxB *e_as_b = &e; + CxxB *ve_as_b = &ve; + + // BREAK(TestCastBaseToDerived) + // BREAK(TestCastDerivedToBase) +} + +// Referenced by TestQualifiedId. +namespace ns { + +int i = 1; + +namespace ns { + +int i = 2; + +} // namespace ns + +} // namespace ns + +static void TestQualifiedId() { + // BREAK(TestQualifiedId) +} + +namespace outer { + +namespace inner { + +class Vars { +public: + inline static double inline_static = 1.5; + static constexpr int static_constexpr = 2; + static const unsigned int static_const; + + struct Nested { + static const int static_const; + }; +}; + +const unsigned int Vars::static_const = 3; +const int Vars::Nested::static_const = 10; + +using MyVars = Vars; + +} // namespace inner + +class Vars { +public: + inline static double inline_static = 4.5; + static constexpr int static_constexpr = 5; + static const unsigned int static_const; + + struct Nested { + static const int static_const; + }; +}; + +const unsigned int Vars::static_const = 6; +const int Vars::Nested::static_const = 20; + +} // namespace outer + +class Vars { +public: + inline static double inline_static = 7.5; + static constexpr int static_constexpr = 8; + static const unsigned int static_const; + + struct Nested { + static const int static_const; + }; +}; + +const unsigned int Vars::static_const = 9; +const int Vars::Nested::static_const = 30; + +static void TestStaticConst() { + Vars vars; + outer::Vars outer_vars; + outer::inner::Vars outer_inner_vars; + + using MyVars = Vars; + using MyOuterVars = outer::Vars; + + MyVars my_vars; + MyOuterVars my_outer_vars; + outer::inner::MyVars my_outer_inner_vars; + + // BREAK(TestStaticConstDeclaredInline) + // BREAK(TestStaticConstDeclaredInlineScoped) + // BREAK(TestStaticConstDeclaredOutsideTheClass) + // BREAK(TestStaticConstDeclaredOutsideTheClassScoped) +} + +// Referenced by TestTemplateTypes. +template struct T_1 { + static const int cx; + typedef double myint; + + T_1() {} + T_1(T x) : x(x) {} + T x; +}; + +template const int T_1::cx = 42; + +template <> const int T_1::cx = 24; + +template struct T_2 { + typedef float myint; + + T_2() {} + T1 x; + T2 y; +}; + +namespace ns { + +template struct T_1 { + static const int cx; + typedef int myint; + + T_1() {} + T_1(T x) : x(x) {} + T x; +}; + +template const int T_1::cx = 46; + +template <> const int T_1::cx = 64; + +} // namespace ns + +static void TestTemplateTypes() { + int i; + int *p = &i; + + { T_1 _; } + { T_1 _; } + { T_1 _; } + { T_1 _(i); } + { T_1 _(p); } + { T_1 _; } + { T_2 _; } + { T_2 _; } + { T_2, T_1> _; } + { T_2>, T_1> _; } + + { ns::T_1 _; } + { ns::T_1> _; } + + { T_1::myint _ = 0; } + { T_1::myint _ = 0; } + { T_1::myint _ = 0; } + { T_1::myint _ = 0; } + { T_1::myint _ = 0; } + { T_1>::myint _ = 0; } + { T_1>>::myint _ = 0; } + { T_1>::myint _ = 0; } + { T_1>::myint _ = 0; } + { T_1>::myint _ = 0; } + { T_1>::myint _ = 0; } + + { T_2::myint _ = 0; } + { T_2::myint _ = 0; } + { T_2::myint _ = 0; } + { T_2>, T_1>::myint _ = 0; } + + { ns::T_1::myint _ = 0; } + { ns::T_1::myint _ = 0; } + { ns::T_1::myint _ = 0; } + { ns::T_1::myint _ = 0; } + { ns::T_1::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + { ns::T_1>::myint _ = 0; } + + (void)T_1::cx; + (void)ns::T_1::cx; + (void)ns::T_1>::cx; + + int T_1 = 2; + + // BREAK(TestTemplateTypes) + // BREAK(TestTemplateCpp11) +} + +template struct TArray { + using ElementType = T; + T t_; + TAllocator a_; +}; + +template struct Allocator { + int size = Size; +}; + +void TestTemplateWithNumericArguments() { + Allocator<4> a4; + Allocator<8> a8; + TArray> arr; + decltype(arr)::ElementType *el = 0; + + // BREAK(TestTemplateWithNumericArguments) +} + +namespace test_scope { + +class Value { +public: + Value(int x, float y) : x_(x), y_(y) {} + + // Static members + enum ValueEnum { A, B }; + static double static_var; + +private: + int x_; + float y_; +}; + +double Value::static_var = 3.5; + +} // namespace test_scope + +void TestValueScope() { + test_scope::Value var(1, 2.5f); + test_scope::Value &var_ref = var; + uint64_t z_ = 3; + + // "raw" representation of the Value. + int bytes[] = {1, 0x40200000}; + + auto val_enum = test_scope::Value::A; + (void)val_enum; + (void)test_scope::Value::static_var; + + // BREAK(TestValueScope) + // BREAK(TestReferenceScope) +} + +void TestBitField() { + enum BitFieldEnum : uint32_t { kZero, kOne }; + + struct BitFieldStruct { + uint16_t a : 10; + uint32_t b : 4; + bool c : 1; + bool d : 1; + int32_t e : 32; + uint32_t f : 32; + uint32_t g : 31; + uint64_t h : 31; + uint64_t i : 33; + BitFieldEnum j : 10; + }; + + BitFieldStruct bf; + bf.a = 0b1111111111; + bf.b = 0b1001; + bf.c = 0b0; + bf.d = 0b1; + bf.e = 0b1; + bf.f = 0b1; + bf.g = 0b1; + bf.h = 0b1; + bf.i = 0b1; + bf.j = BitFieldEnum::kOne; + + struct AlignedBitFieldStruct { + uint16_t a : 10; + uint8_t b : 4; + unsigned char : 0; + uint16_t c : 2; + }; + + uint32_t data = ~0; + AlignedBitFieldStruct abf = (AlignedBitFieldStruct &)data; + + // BREAK(TestBitField) + // BREAK(TestBitFieldScoped) + // BREAK(TestBitFieldPromotion) + // BREAK(TestBitFieldWithSideEffects) +} + +void TestContextVariables() { + struct Scope { + int a = 10; + const char *ptr = "hello"; + }; + + Scope s; + + // BREAK(TestContextVariables) + // BREAK(TestContextVariablesSubset) +} + +// Referenced by TestScopedEnum. +enum class ScopedEnum { kFoo, kBar }; +enum class ScopedEnumUInt8 : uint8_t { kFoo, kBar }; + +void TestScopedEnum() { + auto enum_foo = ScopedEnum::kFoo; + auto enum_bar = ScopedEnum::kBar; + auto enum_neg = (ScopedEnum)-1; + + auto enum_u8_foo = ScopedEnumUInt8::kFoo; + auto enum_u8_bar = ScopedEnumUInt8::kBar; + + // BREAK(TestScopedEnum) + // BREAK(TestScopedEnumArithmetic) + // BREAK(TestScopedEnumWithUnderlyingType) +} + +enum UnscopedEnum { kZero, kOne, kTwo }; +enum UnscopedEnumUInt8 : uint8_t { kZeroU8, kOneU8, kTwoU8 }; +enum UnscopedEnumInt8 : int8_t { kZero8, kOne8, kTwo8 }; +enum UnscopedEnumEmpty : uint8_t {}; + +// UnscopedEnum global_enum = UnscopedEnum::kOne; + +void TestUnscopedEnum() { + auto enum_zero = UnscopedEnum::kZero; + auto enum_one = UnscopedEnum::kOne; + auto enum_two = UnscopedEnum::kTwo; + + auto &enum_one_ref = enum_one; + auto &enum_two_ref = enum_two; + + auto enum_zero_u8 = UnscopedEnumUInt8::kZeroU8; + auto enum_one_u8 = UnscopedEnumUInt8::kOneU8; + auto enum_two_u8 = UnscopedEnumUInt8::kTwoU8; + + UnscopedEnumEmpty enum_empty{}; + + auto enum_one_8 = UnscopedEnumInt8::kOne8; + auto enum_neg_8 = (UnscopedEnumInt8)-1; + + // BREAK(TestUnscopedEnum) + // BREAK(TestUnscopedEnumNegation) + // BREAK(TestUnscopedEnumWithUnderlyingType) + // BREAK(TestUnscopedEnumEmpty) +} + +void TestTernaryOperator() { + int i = 1; + int *pi = &i; + char c = 2; + int arr2[2] = {1, 2}; + int arr3[3] = {1, 2, 3}; + double dbl_arr[2] = {1.0, 2.0}; + struct T { + } t; + enum EnumA { kOneA = 1, kTwoA } a_enum = kTwoA; + enum EnumB { kOneB = 1 } b_enum = kOneB; + // BREAK(TestTernaryOperator) +} + +void TestSizeOf() { + int i = 1; + int *p = &i; + int arr[] = {1, 2, 3}; + + struct SizeOfFoo { + int x, y; + } foo; + + // BREAK(TestSizeOf) +} + +void TestBuiltinFunction_Log2() { + struct Foo { + } foo; + + enum CEnum { kFoo = 129 } c_enum = kFoo; + enum class CxxEnum { kFoo = 129 } cxx_enum = CxxEnum::kFoo; + + CEnum &c_enum_ref = c_enum; + CxxEnum &cxx_enum_ref = cxx_enum; + + // BREAK(TestBuiltinFunction_Log2) +} + +void TestBuiltinFunction_findnonnull() { + uint8_t array_of_uint8[] = {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}; + uint8_t *pointer_to_uint8 = array_of_uint8; + + int *array_of_pointers[] = {(int *)1, (int *)1, (int *)0, (int *)0, (int *)1}; + int **pointer_to_pointers = array_of_pointers; + + // BREAK(TestBuiltinFunction_findnonnull) +} + +void TestPrefixIncDec() { + auto enum_foo = ScopedEnum::kFoo; + int i = 1; + + // BREAK(TestPrefixIncDec) + // BREAK(TestPostfixIncDec) +} + +void TestDereferencedType() { + struct TTuple { + int x = 1; + }; + using TPair = TTuple; + + TPair p{}; + const TPair &p_ref = p; + const TPair *p_ptr = &p; + + // BREAK(TestDereferencedType) +} + +void TestMemberFunctionCall() { + struct C { + int m() { return 1; } + }; + + C c; + c.m(); + + // BREAK(TestMemberFunctionCall) +} + +void TestCompositeAssignment() { + int i = 10; + float f = 1.5f; + float *p = &f; + + enum Enum { ONE, TWO }; + Enum eOne = ONE; + Enum eTwo = TWO; + + // BREAK(TestAssignment) + // BREAK(TestCompositeAssignmentInvalid) + // BREAK(TestCompositeAssignmentAdd) + // BREAK(TestCompositeAssignmentSub) + // BREAK(TestCompositeAssignmentMul) + // BREAK(TestCompositeAssignmentDiv) + // BREAK(TestCompositeAssignmentRem) + // BREAK(TestCompositeAssignmentBitwise) +} + +void TestSideEffects() { + int x = 1; + int xa[] = {1, 2}; + int *p = &x; + + // BREAK(TestSideEffects) +} + +void TestUniquePtr() { + struct NodeU { + std::unique_ptr next; + int value; + }; + auto ptr_node = std::unique_ptr(new NodeU{nullptr, 2}); + ptr_node = std::unique_ptr(new NodeU{std::move(ptr_node), 1}); + + std::unique_ptr ptr_null; + auto ptr_int = std::make_unique(1); + auto ptr_float = std::make_unique(1.1f); + + auto deleter = [](void const *data) { + delete static_cast(data); + }; + std::unique_ptr ptr_void(new int(42), deleter); + + // BREAK(TestUniquePtr) + // BREAK(TestUniquePtrDeref) + // BREAK(TestUniquePtrCompare) +} + +void TestSharedPtr() { + struct NodeS { + std::shared_ptr next; + int value; + }; + auto ptr_node = std::shared_ptr(new NodeS{nullptr, 2}); + ptr_node = std::shared_ptr(new NodeS{std::move(ptr_node), 1}); + + std::shared_ptr ptr_null; + auto ptr_int = std::make_shared(1); + auto ptr_float = std::make_shared(1.1f); + + std::weak_ptr ptr_int_weak = ptr_int; + + std::shared_ptr ptr_void = ptr_int; + + // BREAK(TestSharedPtr) + // BREAK(TestSharedPtrDeref) + // BREAK(TestSharedPtrCompare) +} + +void TestTypeComparison() { + int i = 1; + int const *const icpc = &i; + int *ip = &i; + int const *const *const icpcpc = &icpc; + int **ipp = &ip; + + using MyInt = int; + using MyPtr = MyInt *; + MyPtr *mipp = ipp; + + using MyConstInt = const int; + using MyConstPtr = MyConstInt *const; + MyConstPtr *const micpcpc = icpcpc; + + char c = 2; + signed char sc = 65; + const char cc = 66; + using mychar = char; + mychar mc = 67; + + // BREAK(TestTypeComparison) +} + +static void TestTypeDeclaration() { + wchar_t wchar = 0; + char16_t char16 = 0; + char32_t char32 = 0; + + using mylong = long; + mylong my_long = 1; + + // BREAK(TestBasicTypeDeclaration) + // BREAK(TestUserTypeDeclaration) +} + +static void TestTypeVsIdentifier() { + struct StructOrVar { + int x = 1; + } s; + short StructOrVar = 2; + + class ClassOrVar { + public: + int x = 3; + }; + ClassOrVar ClassOrVar; + + union UnionOrVar { + int x; + } u; + int UnionOrVar[2] = {1, 2}; + + enum EnumOrVar { kFoo, kBar }; + EnumOrVar EnumOrVar = kFoo; + + enum class CxxEnumOrVar { kCxxFoo, kCxxBar }; + CxxEnumOrVar CxxEnumOrVar = CxxEnumOrVar::kCxxFoo; + + int OnlyVar = 4; + + // BREAK(TestTypeVsIdentifier) +} + +static void TestSeparateParsing() { + struct StructA { + int a_; + } a{1}; + + struct StructB { + int b_; + } b{2}; + + struct StructC : public StructA, public StructB { + int c_; + } c{{3}, {4}, 5}; + + struct StructD : public StructC { + int d_; + } d{{{6}, {7}, 8}, 9}; + + // BREAK(TestSeparateParsing) + // BREAK(TestSeparateParsingWithContextVars) +} + +// Used by TestRegistersNoDollar +int rcx = 42; + +struct RegisterCtx { + int rbx = 42; + + void TestRegisters() { + int rax = 42; + + // BREAK(TestRegisters) + // BREAK(TestRegistersNoDollar) + } +}; + +static void TestCharParsing() { + // BREAK(TestCharParsing) +} + +static void TestStringParsing() { + // BREAK(TestStringParsing) +} + +namespace test_binary { + +void main() { + // BREAK(TestSymbols) + + TestMethods tm; + + TestArithmetic(); + TestBitwiseOperators(); + TestPointerArithmetic(); + TestLogicalOperators(); + TestLocalVariables(); + TestMemberOf(); + TestMemberOfInheritance(); + TestMemberOfAnonymousMember(); + TestGlobalVariableLookup(); + tm.TestInstanceVariables(); + TestIndirection(); + tm.TestAddressOf(42); + TestSubscript(); + TestCStyleCast(); + TestCxxCast(); + TestCastInheritedTypes(); + TestQualifiedId(); + TestStaticConst(); + TestTypeDeclaration(); + TestTemplateTypes(); + TestTemplateWithNumericArguments(); + TestValueScope(); + TestBitField(); + TestContextVariables(); + TestPrefixIncDec(); + TestScopedEnum(); + TestUnscopedEnum(); + TestTernaryOperator(); + TestSizeOf(); + TestBuiltinFunction_Log2(); + TestBuiltinFunction_findnonnull(); + TestArrayDereference(); + TestDereferencedType(); + TestMemberFunctionCall(); + TestCompositeAssignment(); + TestSideEffects(); + TestUniquePtr(); + TestSharedPtr(); + TestTypeComparison(); + TestTypeVsIdentifier(); + TestSeparateParsing(); + + RegisterCtx rc; + rc.TestRegisters(); + + TestCharParsing(); + TestStringParsing(); + + // BREAK HERE +} + +} // namespace test_binary + +int main() { test_binary::main(); } diff --git a/lldb/unittests/DIL/Inputs/test_library.cc b/lldb/unittests/DIL/Inputs/test_library.cc new file mode 100644 index 000000000000..a3905e6e9f23 --- /dev/null +++ b/lldb/unittests/DIL/Inputs/test_library.cc @@ -0,0 +1,15 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +int externGlobalVar = 0x00C0FFEE; diff --git a/lldb/unittests/DIL/Runner.cpp b/lldb/unittests/DIL/Runner.cpp new file mode 100644 index 000000000000..691ab747c1b3 --- /dev/null +++ b/lldb/unittests/DIL/Runner.cpp @@ -0,0 +1,152 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "Runner.h" + +#include +#include +#include + +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBValue.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-types.h" + +#ifdef CONFIG_VALGRIND +// Running a process under Valgrind can be extremely slow. +const uint32_t kWaitForEventTimeout = 30; +#else +// Running a process can be slow when built with sanitizers. +const uint32_t kWaitForEventTimeout = 5; +#endif + +int FindBreakpointLine(const std::string &file_path, + const std::string &break_line) { + // Read the source file to find the breakpoint location. + std::ifstream infile(file_path); + std::string line; + int line_num = 1; + while (std::getline(infile, line)) { + if (line.find(break_line) != std::string::npos) { + return line_num; + } + ++line_num; + } + + std::cerr << "Can't find the breakpoint location." << std::endl; + exit(1); +} + +std::string filename_of_source_path(const std::string &source_path) { + auto idx = source_path.find_last_of("/\\"); + if (idx == std::string::npos) { + idx = 0; + } else { + idx++; + } + + return source_path.substr(idx); +} + +lldb::SBProcess LaunchTestProgram(lldb::SBDebugger debugger, + const std::string &source_path, + const std::string &binary_path, + const std::string &break_line) { + auto target = debugger.CreateTarget(binary_path.c_str()); + + auto source_file = filename_of_source_path(source_path); + + const char *argv[] = {binary_path.c_str(), nullptr}; + + auto bp = target.BreakpointCreateByLocation( + source_file.c_str(), FindBreakpointLine(source_path.c_str(), break_line)); + // Test programs don't perform any I/O, so current directory doesn't + // matter. + if (bp.GetNumLocations() == 0) + std::cerr + << "WARNING: Unable to resolve breakpoint to any actual locations." + << std::endl; + auto process = target.LaunchSimple(argv, nullptr, "."); + if (!process.IsValid()) { + std::cerr << "ERROR: Unable to launch process. Check that the path to the " + "binary is valid." + << std::endl; + return process; + } + lldb::SBEvent event; + auto listener = debugger.GetListener(); + + while (true) { + if (!listener.WaitForEvent(kWaitForEventTimeout, event)) { + std::cerr + << "Timeout while waiting for the event, kill the process and exit." + << std::endl; + process.Destroy(); + exit(1); + } + + if (!lldb::SBProcess::EventIsProcessEvent(event)) { + std::cerr << "Got some random event: " + << lldb::SBEvent::GetCStringFromEvent(event) << std::endl; + continue; + } + + auto state = lldb::SBProcess::GetStateFromEvent(event); + if (state == lldb::eStateInvalid) { + std::cerr << "process event: " + << lldb::SBEvent::GetCStringFromEvent(event) << std::endl; + continue; + } + + if (state == lldb::eStateExited) { + std::cerr << "Process exited: " << process.GetExitStatus() << std::endl; + process.Destroy(); + exit(1); + } + + if (state != lldb::eStateStopped) { + continue; + } + + auto thread = process.GetSelectedThread(); + auto stopReason = thread.GetStopReason(); + + if (stopReason != lldb::eStopReasonBreakpoint) { + continue; + } + + auto bpId = + static_cast(thread.GetStopReasonDataAtIndex(0)); + if (bpId != bp.GetID()) { + std::cerr << "Stopped at unknown breakpoint: " << bpId << std::endl + << "Now killing process and exiting" << std::endl; + process.Destroy(); + exit(1); + } + + return process; + } +} diff --git a/lldb/unittests/DIL/Runner.h b/lldb/unittests/DIL/Runner.h new file mode 100644 index 000000000000..a1333fb950bc --- /dev/null +++ b/lldb/unittests/DIL/Runner.h @@ -0,0 +1,28 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LLDB_DIL_RUNNER_H_ +#define LLDB_DIL_RUNNER_H_ + +#include + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBProcess.h" + +lldb::SBProcess LaunchTestProgram(lldb::SBDebugger debugger, + const std::string &source_path, + const std::string &binary_path, + const std::string &break_line); + +#endif // LLDB_DIL_RUNNER_H_ \ No newline at end of file diff --git a/lldb/unittests/DIL/Traits.h b/lldb/unittests/DIL/Traits.h new file mode 100644 index 000000000000..0aece0cf7b4d --- /dev/null +++ b/lldb/unittests/DIL/Traits.h @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +// Template magic to check whether a class has a specific method. +template +constexpr auto is_valid(F &&f) -> decltype(f(std::declval()), true) { + return true; +} +template constexpr bool is_valid(...) { return false; } +#define HAS_METHOD(T, EXPR) is_valid([](auto &&obj) -> decltype(obj.EXPR) {})