278 lines · cpp
1//===---- ModernizeModuleTest.cpp - clang-tidy ----------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8#include "ClangTidyTest.h"9#include "modernize/IntegralLiteralExpressionMatcher.h"10#include "clang/Lex/Lexer.h"11#include "gtest/gtest.h"12 13#include <cstring>14#include <iterator>15#include <string>16#include <vector>17 18namespace clang {19namespace tidy {20namespace test {21 22static std::vector<Token> tokenify(const char *Text) {23 LangOptions LangOpts;24 std::vector<std::string> Includes;25 LangOptions::setLangDefaults(LangOpts, Language::CXX, llvm::Triple(),26 Includes, LangStandard::lang_cxx20);27 Lexer Lex(SourceLocation{}, LangOpts, Text, Text, Text + std::strlen(Text));28 std::vector<Token> Tokens;29 bool End = false;30 while (!End) {31 Token Tok;32 End = Lex.LexFromRawLexer(Tok);33 Tokens.push_back(Tok);34 }35 36 return Tokens;37}38 39static bool matchText(const char *Text, bool AllowComma) {40 std::vector<Token> Tokens{tokenify(Text)};41 modernize::IntegralLiteralExpressionMatcher Matcher(Tokens, AllowComma);42 43 return Matcher.match();44}45 46static modernize::LiteralSize sizeText(const char *Text) {47 std::vector<Token> Tokens{tokenify(Text)};48 modernize::IntegralLiteralExpressionMatcher Matcher(Tokens, true);49 if (Matcher.match())50 return Matcher.largestLiteralSize();51 return modernize::LiteralSize::Unknown;52}53 54static const char *toString(modernize::LiteralSize Value) {55 switch (Value) {56 case modernize::LiteralSize::Int:57 return "Int";58 case modernize::LiteralSize::UnsignedInt:59 return "UnsignedInt";60 case modernize::LiteralSize::Long:61 return "Long";62 case modernize::LiteralSize::UnsignedLong:63 return "UnsignedLong";64 case modernize::LiteralSize::LongLong:65 return "LongLong";66 case modernize::LiteralSize::UnsignedLongLong:67 return "UnsignedLongLong";68 default:69 return "Unknown";70 }71}72 73namespace {74 75struct MatchParam {76 bool AllowComma;77 bool Matched;78 const char *Text;79 80 friend std::ostream &operator<<(std::ostream &Str, const MatchParam &Value) {81 return Str << "Allow operator,: " << std::boolalpha << Value.AllowComma82 << ", Matched: " << std::boolalpha << Value.Matched83 << ", Text: '" << Value.Text << '\'';84 }85};86 87struct SizeParam {88 modernize::LiteralSize Size;89 const char *Text;90 91 friend std::ostream &operator<<(std::ostream &Str, const SizeParam &Value) {92 return Str << "Size: " << toString(Value.Size) << ", Text: '" << Value.Text << '\'';93 }94};95 96class MatcherTest : public ::testing::TestWithParam<MatchParam> {};97 98class SizeTest : public ::testing::TestWithParam<SizeParam> {};99 100} // namespace101 102static const MatchParam MatchParams[] = {103 // Accept integral literals.104 {true, true, "1"},105 {true, true, "0177"},106 {true, true, "0xdeadbeef"},107 {true, true, "0b1011"},108 {true, true, "'c'"},109 // Reject non-integral literals.110 {true, false, "1.23"},111 {true, false, "0x1p3"},112 {true, false, R"("string")"},113 {true, false, "1i"},114 115 // Accept literals with these unary operators.116 {true, true, "-1"},117 {true, true, "+1"},118 {true, true, "~1"},119 {true, true, "!1"},120 // Reject invalid unary operators.121 {true, false, "1-"},122 {true, false, "1+"},123 {true, false, "1~"},124 {true, false, "1!"},125 126 // Accept valid binary operators.127 {true, true, "1+1"},128 {true, true, "1-1"},129 {true, true, "1*1"},130 {true, true, "1/1"},131 {true, true, "1%2"},132 {true, true, "1<<1"},133 {true, true, "1>>1"},134 {true, true, "1<=>1"},135 {true, true, "1<1"},136 {true, true, "1>1"},137 {true, true, "1<=1"},138 {true, true, "1>=1"},139 {true, true, "1==1"},140 {true, true, "1!=1"},141 {true, true, "1&1"},142 {true, true, "1^1"},143 {true, true, "1|1"},144 {true, true, "1&&1"},145 {true, true, "1||1"},146 {true, true, "1+ +1"}, // A space is needed to avoid being tokenized as ++ or --.147 {true, true, "1- -1"},148 // Comma is only valid when inside parentheses.149 {true, true, "(1,1)"},150 // Reject invalid binary operators.151 {true, false, "1+"},152 {true, false, "1-"},153 {true, false, "1*"},154 {true, false, "1/"},155 {true, false, "1%"},156 {true, false, "1<<"},157 {true, false, "1>>"},158 {true, false, "1<=>"},159 {true, false, "1<"},160 {true, false, "1>"},161 {true, false, "1<="},162 {true, false, "1>="},163 {true, false, "1=="},164 {true, false, "1!="},165 {true, false, "1&"},166 {true, false, "1^"},167 {true, false, "1|"},168 {true, false, "1&&"},169 {true, false, "1||"},170 {true, false, "1,"},171 {true, false, ",1"},172 {true, false, "1,1"},173 174 // Accept valid ternary operators.175 {true, true, "1?1:1"},176 {true, true, "1?:1"}, // A gcc extension treats x ? : y as x ? x : y.177 // Reject invalid ternary operators.178 {true, false, "?"},179 {true, false, "?1"},180 {true, false, "?:"},181 {true, false, "?:1"},182 {true, false, "?1:"},183 {true, false, "?1:1"},184 {true, false, "1?"},185 {true, false, "1?1"},186 {true, false, "1?:"},187 {true, false, "1?1:"},188 189 // Accept parenthesized expressions.190 {true, true, "(1)"},191 {true, true, "((+1))"},192 {true, true, "((+(1)))"},193 {true, true, "(-1)"},194 {true, true, "-(1)"},195 {true, true, "(+1)"},196 {true, true, "((+1))"},197 {true, true, "+(1)"},198 {true, true, "(~1)"},199 {true, true, "~(1)"},200 {true, true, "(!1)"},201 {true, true, "!(1)"},202 {true, true, "(1+1)"},203 {true, true, "(1-1)"},204 {true, true, "(1*1)"},205 {true, true, "(1/1)"},206 {true, true, "(1%2)"},207 {true, true, "(1<<1)"},208 {true, true, "(1>>1)"},209 {true, true, "(1<=>1)"},210 {true, true, "(1<1)"},211 {true, true, "(1>1)"},212 {true, true, "(1<=1)"},213 {true, true, "(1>=1)"},214 {true, true, "(1==1)"},215 {true, true, "(1!=1)"},216 {true, true, "(1&1)"},217 {true, true, "(1^1)"},218 {true, true, "(1|1)"},219 {true, true, "(1&&1)"},220 {true, true, "(1||1)"},221 {true, true, "(1?1:1)"},222 223 // Accept more complicated "chained" expressions.224 {true, true, "1+1+1"},225 {true, true, "1+1+1+1"},226 {true, true, "1+1+1+1+1"},227 {true, true, "1*1*1"},228 {true, true, "1*1*1*1"},229 {true, true, "1*1*1*1*1"},230 {true, true, "1<<1<<1"},231 {true, true, "4U>>1>>1"},232 {true, true, "1<1<1"},233 {true, true, "1>1>1"},234 {true, true, "1<=1<=1"},235 {true, true, "1>=1>=1"},236 {true, true, "1==1==1"},237 {true, true, "1!=1!=1"},238 {true, true, "1&1&1"},239 {true, true, "1^1^1"},240 {true, true, "1|1|1"},241 {true, true, "1&&1&&1"},242 {true, true, "1||1||1"},243 {true, true, "(1,1,1)"},244 245 // Optionally reject comma operator246 {false, false, "1,1"}247};248 249TEST_P(MatcherTest, MatchResult) {250 const MatchParam &Param = GetParam();251 252 EXPECT_TRUE(matchText(Param.Text, Param.AllowComma) == Param.Matched);253}254 255INSTANTIATE_TEST_SUITE_P(IntegralLiteralExpressionMatcherTests, MatcherTest,256 ::testing::ValuesIn(MatchParams));257 258static const SizeParam SizeParams[] = {259 {modernize::LiteralSize::Int, "1"},260 {modernize::LiteralSize::UnsignedInt, "1U"},261 {modernize::LiteralSize::Long, "1L"},262 {modernize::LiteralSize::UnsignedLong, "1UL"},263 {modernize::LiteralSize::UnsignedLong, "1LU"},264 {modernize::LiteralSize::LongLong, "1LL"},265 {modernize::LiteralSize::UnsignedLongLong, "1ULL"},266 {modernize::LiteralSize::UnsignedLongLong, "1LLU"}};267 268TEST_P(SizeTest, TokenSize) {269 EXPECT_EQ(sizeText(GetParam().Text), GetParam().Size);270}271 272INSTANTIATE_TEST_SUITE_P(IntegralLiteralExpressionMatcherTests, SizeTest,273 ::testing::ValuesIn(SizeParams));274 275} // namespace test276} // namespace tidy277} // namespace clang278