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-exception 6 // 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 18 namespace clang { 19 namespace tidy { 20 namespace test { 21 22 static 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 39 static 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 46 static 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 54 static 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 73 namespace { 74 75 struct 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.AllowComma 82 << ", Matched: " << std::boolalpha << Value.Matched 83 << ", Text: '" << Value.Text << '\''; 84 } 85 }; 86 87 struct 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 96 class MatcherTest : public ::testing::TestWithParam<MatchParam> {}; 97 98 class SizeTest : public ::testing::TestWithParam<SizeParam> {}; 99 100 } // namespace 101 102 static 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 operator 246 {false, false, "1,1"} 247 }; 248 249 TEST_P(MatcherTest, MatchResult) { 250 const MatchParam &Param = GetParam(); 251 252 EXPECT_TRUE(matchText(Param.Text, Param.AllowComma) == Param.Matched); 253 } 254 255 INSTANTIATE_TEST_SUITE_P(IntegralLiteralExpressionMatcherTests, MatcherTest, 256 ::testing::ValuesIn(MatchParams)); 257 258 static 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 268 TEST_P(SizeTest, TokenSize) { 269 EXPECT_EQ(sizeText(GetParam().Text), GetParam().Size); 270 }; 271 272 INSTANTIATE_TEST_SUITE_P(IntegralLiteralExpressionMatcherTests, SizeTest, 273 ::testing::ValuesIn(SizeParams)); 274 275 } // namespace test 276 } // namespace tidy 277 } // namespace clang 278