//===---- 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 #include #include #include namespace clang { namespace tidy { namespace test { static std::vector tokenify(const char *Text) { LangOptions LangOpts; std::vector Includes; LangOptions::setLangDefaults(LangOpts, Language::CXX, llvm::Triple(), Includes, LangStandard::lang_cxx20); Lexer Lex(SourceLocation{}, LangOpts, Text, Text, Text + std::strlen(Text)); std::vector 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 Tokens{tokenify(Text)}; modernize::IntegralLiteralExpressionMatcher Matcher(Tokens, AllowComma); return Matcher.match(); } static modernize::LiteralSize sizeText(const char *Text) { std::vector 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 {}; class SizeTest : public ::testing::TestWithParam {}; } // 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