blob: 4a8c1c3e5f3126ae394b83b30a97e6ffa8b6babd [file] [log] [blame]
//===---- ModernizeModuleTest.cpp - clang-tidy ----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "ClangTidyTest.h"
#include "modernize/IntegralLiteralExpressionMatcher.h"
#include "clang/Lex/Lexer.h"
#include "gtest/gtest.h"
#include <cstring>
#include <iterator>
#include <string>
#include <vector>
namespace clang {
namespace tidy {
namespace test {
static std::vector<Token> tokenify(const char *Text) {
LangOptions LangOpts;
std::vector<std::string> Includes;
LangOptions::setLangDefaults(LangOpts, Language::CXX, llvm::Triple(),
Includes, LangStandard::lang_cxx20);
Lexer Lex(SourceLocation{}, LangOpts, Text, Text, Text + std::strlen(Text));
std::vector<Token> Tokens;
bool End = false;
while (!End) {
Token Tok;
End = Lex.LexFromRawLexer(Tok);
Tokens.push_back(Tok);
}
return Tokens;
}
static bool matchText(const char *Text, bool AllowComma) {
std::vector<Token> Tokens{tokenify(Text)};
modernize::IntegralLiteralExpressionMatcher Matcher(Tokens, AllowComma);
return Matcher.match();
}
static modernize::LiteralSize sizeText(const char *Text) {
std::vector<Token> Tokens{tokenify(Text)};
modernize::IntegralLiteralExpressionMatcher Matcher(Tokens, true);
if (Matcher.match())
return Matcher.largestLiteralSize();
return modernize::LiteralSize::Unknown;
}
static const char *toString(modernize::LiteralSize Value) {
switch (Value) {
case modernize::LiteralSize::Int:
return "Int";
case modernize::LiteralSize::UnsignedInt:
return "UnsignedInt";
case modernize::LiteralSize::Long:
return "Long";
case modernize::LiteralSize::UnsignedLong:
return "UnsignedLong";
case modernize::LiteralSize::LongLong:
return "LongLong";
case modernize::LiteralSize::UnsignedLongLong:
return "UnsignedLongLong";
default:
return "Unknown";
}
}
namespace {
struct MatchParam {
bool AllowComma;
bool Matched;
const char *Text;
friend std::ostream &operator<<(std::ostream &Str, const MatchParam &Value) {
return Str << "Allow operator,: " << std::boolalpha << Value.AllowComma
<< ", Matched: " << std::boolalpha << Value.Matched
<< ", Text: '" << Value.Text << '\'';
}
};
struct SizeParam {
modernize::LiteralSize Size;
const char *Text;
friend std::ostream &operator<<(std::ostream &Str, const SizeParam &Value) {
return Str << "Size: " << toString(Value.Size) << ", Text: '" << Value.Text << '\'';
}
};
class MatcherTest : public ::testing::TestWithParam<MatchParam> {};
class SizeTest : public ::testing::TestWithParam<SizeParam> {};
} // namespace
static const MatchParam MatchParams[] = {
// Accept integral literals.
{true, true, "1"},
{true, true, "0177"},
{true, true, "0xdeadbeef"},
{true, true, "0b1011"},
{true, true, "'c'"},
// Reject non-integral literals.
{true, false, "1.23"},
{true, false, "0x1p3"},
{true, false, R"("string")"},
{true, false, "1i"},
// Accept literals with these unary operators.
{true, true, "-1"},
{true, true, "+1"},
{true, true, "~1"},
{true, true, "!1"},
// Reject invalid unary operators.
{true, false, "1-"},
{true, false, "1+"},
{true, false, "1~"},
{true, false, "1!"},
// Accept valid binary operators.
{true, true, "1+1"},
{true, true, "1-1"},
{true, true, "1*1"},
{true, true, "1/1"},
{true, true, "1%2"},
{true, true, "1<<1"},
{true, true, "1>>1"},
{true, true, "1<=>1"},
{true, true, "1<1"},
{true, true, "1>1"},
{true, true, "1<=1"},
{true, true, "1>=1"},
{true, true, "1==1"},
{true, true, "1!=1"},
{true, true, "1&1"},
{true, true, "1^1"},
{true, true, "1|1"},
{true, true, "1&&1"},
{true, true, "1||1"},
{true, true, "1+ +1"}, // A space is needed to avoid being tokenized as ++ or --.
{true, true, "1- -1"},
// Comma is only valid when inside parentheses.
{true, true, "(1,1)"},
// Reject invalid binary operators.
{true, false, "1+"},
{true, false, "1-"},
{true, false, "1*"},
{true, false, "1/"},
{true, false, "1%"},
{true, false, "1<<"},
{true, false, "1>>"},
{true, false, "1<=>"},
{true, false, "1<"},
{true, false, "1>"},
{true, false, "1<="},
{true, false, "1>="},
{true, false, "1=="},
{true, false, "1!="},
{true, false, "1&"},
{true, false, "1^"},
{true, false, "1|"},
{true, false, "1&&"},
{true, false, "1||"},
{true, false, "1,"},
{true, false, ",1"},
{true, false, "1,1"},
// Accept valid ternary operators.
{true, true, "1?1:1"},
{true, true, "1?:1"}, // A gcc extension treats x ? : y as x ? x : y.
// Reject invalid ternary operators.
{true, false, "?"},
{true, false, "?1"},
{true, false, "?:"},
{true, false, "?:1"},
{true, false, "?1:"},
{true, false, "?1:1"},
{true, false, "1?"},
{true, false, "1?1"},
{true, false, "1?:"},
{true, false, "1?1:"},
// Accept parenthesized expressions.
{true, true, "(1)"},
{true, true, "((+1))"},
{true, true, "((+(1)))"},
{true, true, "(-1)"},
{true, true, "-(1)"},
{true, true, "(+1)"},
{true, true, "((+1))"},
{true, true, "+(1)"},
{true, true, "(~1)"},
{true, true, "~(1)"},
{true, true, "(!1)"},
{true, true, "!(1)"},
{true, true, "(1+1)"},
{true, true, "(1-1)"},
{true, true, "(1*1)"},
{true, true, "(1/1)"},
{true, true, "(1%2)"},
{true, true, "(1<<1)"},
{true, true, "(1>>1)"},
{true, true, "(1<=>1)"},
{true, true, "(1<1)"},
{true, true, "(1>1)"},
{true, true, "(1<=1)"},
{true, true, "(1>=1)"},
{true, true, "(1==1)"},
{true, true, "(1!=1)"},
{true, true, "(1&1)"},
{true, true, "(1^1)"},
{true, true, "(1|1)"},
{true, true, "(1&&1)"},
{true, true, "(1||1)"},
{true, true, "(1?1:1)"},
// Accept more complicated "chained" expressions.
{true, true, "1+1+1"},
{true, true, "1+1+1+1"},
{true, true, "1+1+1+1+1"},
{true, true, "1*1*1"},
{true, true, "1*1*1*1"},
{true, true, "1*1*1*1*1"},
{true, true, "1<<1<<1"},
{true, true, "4U>>1>>1"},
{true, true, "1<1<1"},
{true, true, "1>1>1"},
{true, true, "1<=1<=1"},
{true, true, "1>=1>=1"},
{true, true, "1==1==1"},
{true, true, "1!=1!=1"},
{true, true, "1&1&1"},
{true, true, "1^1^1"},
{true, true, "1|1|1"},
{true, true, "1&&1&&1"},
{true, true, "1||1||1"},
{true, true, "(1,1,1)"},
// Optionally reject comma operator
{false, false, "1,1"}
};
TEST_P(MatcherTest, MatchResult) {
const MatchParam &Param = GetParam();
EXPECT_TRUE(matchText(Param.Text, Param.AllowComma) == Param.Matched);
}
INSTANTIATE_TEST_SUITE_P(IntegralLiteralExpressionMatcherTests, MatcherTest,
::testing::ValuesIn(MatchParams));
static const SizeParam SizeParams[] = {
{modernize::LiteralSize::Int, "1"},
{modernize::LiteralSize::UnsignedInt, "1U"},
{modernize::LiteralSize::Long, "1L"},
{modernize::LiteralSize::UnsignedLong, "1UL"},
{modernize::LiteralSize::UnsignedLong, "1LU"},
{modernize::LiteralSize::LongLong, "1LL"},
{modernize::LiteralSize::UnsignedLongLong, "1ULL"},
{modernize::LiteralSize::UnsignedLongLong, "1LLU"}};
TEST_P(SizeTest, TokenSize) {
EXPECT_EQ(sizeText(GetParam().Text), GetParam().Size);
}
INSTANTIATE_TEST_SUITE_P(IntegralLiteralExpressionMatcherTests, SizeTest,
::testing::ValuesIn(SizeParams));
} // namespace test
} // namespace tidy
} // namespace clang