| 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(Opts&: LangOpts, Lang: Language::CXX, T: llvm::Triple(), |
| 26 | Includes, LangStd: LangStandard::lang_cxx20); |
| 27 | Lexer Lex(SourceLocation{}, LangOpts, Text, Text, Text + std::strlen(s: Text)); |
| 28 | std::vector<Token> Tokens; |
| 29 | bool End = false; |
| 30 | while (!End) { |
| 31 | Token Tok; |
| 32 | End = Lex.LexFromRawLexer(Result&: Tok); |
| 33 | Tokens.push_back(x: 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: 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 | {.AllowComma: true, .Matched: true, .Text: "1" }, |
| 105 | {.AllowComma: true, .Matched: true, .Text: "0177" }, |
| 106 | {.AllowComma: true, .Matched: true, .Text: "0xdeadbeef" }, |
| 107 | {.AllowComma: true, .Matched: true, .Text: "0b1011" }, |
| 108 | {.AllowComma: true, .Matched: true, .Text: "'c'" }, |
| 109 | // Reject non-integral literals. |
| 110 | {.AllowComma: true, .Matched: false, .Text: "1.23" }, |
| 111 | {.AllowComma: true, .Matched: false, .Text: "0x1p3" }, |
| 112 | {.AllowComma: true, .Matched: false, .Text: R"("string")" }, |
| 113 | {.AllowComma: true, .Matched: false, .Text: "1i" }, |
| 114 | |
| 115 | // Accept literals with these unary operators. |
| 116 | {.AllowComma: true, .Matched: true, .Text: "-1" }, |
| 117 | {.AllowComma: true, .Matched: true, .Text: "+1" }, |
| 118 | {.AllowComma: true, .Matched: true, .Text: "~1" }, |
| 119 | {.AllowComma: true, .Matched: true, .Text: "!1" }, |
| 120 | // Reject invalid unary operators. |
| 121 | {.AllowComma: true, .Matched: false, .Text: "1-" }, |
| 122 | {.AllowComma: true, .Matched: false, .Text: "1+" }, |
| 123 | {.AllowComma: true, .Matched: false, .Text: "1~" }, |
| 124 | {.AllowComma: true, .Matched: false, .Text: "1!" }, |
| 125 | |
| 126 | // Accept valid binary operators. |
| 127 | {.AllowComma: true, .Matched: true, .Text: "1+1" }, |
| 128 | {.AllowComma: true, .Matched: true, .Text: "1-1" }, |
| 129 | {.AllowComma: true, .Matched: true, .Text: "1*1" }, |
| 130 | {.AllowComma: true, .Matched: true, .Text: "1/1" }, |
| 131 | {.AllowComma: true, .Matched: true, .Text: "1%2" }, |
| 132 | {.AllowComma: true, .Matched: true, .Text: "1<<1" }, |
| 133 | {.AllowComma: true, .Matched: true, .Text: "1>>1" }, |
| 134 | {.AllowComma: true, .Matched: true, .Text: "1<=>1" }, |
| 135 | {.AllowComma: true, .Matched: true, .Text: "1<1" }, |
| 136 | {.AllowComma: true, .Matched: true, .Text: "1>1" }, |
| 137 | {.AllowComma: true, .Matched: true, .Text: "1<=1" }, |
| 138 | {.AllowComma: true, .Matched: true, .Text: "1>=1" }, |
| 139 | {.AllowComma: true, .Matched: true, .Text: "1==1" }, |
| 140 | {.AllowComma: true, .Matched: true, .Text: "1!=1" }, |
| 141 | {.AllowComma: true, .Matched: true, .Text: "1&1" }, |
| 142 | {.AllowComma: true, .Matched: true, .Text: "1^1" }, |
| 143 | {.AllowComma: true, .Matched: true, .Text: "1|1" }, |
| 144 | {.AllowComma: true, .Matched: true, .Text: "1&&1" }, |
| 145 | {.AllowComma: true, .Matched: true, .Text: "1||1" }, |
| 146 | {.AllowComma: true, .Matched: true, .Text: "1+ +1" }, // A space is needed to avoid being tokenized as ++ or --. |
| 147 | {.AllowComma: true, .Matched: true, .Text: "1- -1" }, |
| 148 | // Comma is only valid when inside parentheses. |
| 149 | {.AllowComma: true, .Matched: true, .Text: "(1,1)" }, |
| 150 | // Reject invalid binary operators. |
| 151 | {.AllowComma: true, .Matched: false, .Text: "1+" }, |
| 152 | {.AllowComma: true, .Matched: false, .Text: "1-" }, |
| 153 | {.AllowComma: true, .Matched: false, .Text: "1*" }, |
| 154 | {.AllowComma: true, .Matched: false, .Text: "1/" }, |
| 155 | {.AllowComma: true, .Matched: false, .Text: "1%" }, |
| 156 | {.AllowComma: true, .Matched: false, .Text: "1<<" }, |
| 157 | {.AllowComma: true, .Matched: false, .Text: "1>>" }, |
| 158 | {.AllowComma: true, .Matched: false, .Text: "1<=>" }, |
| 159 | {.AllowComma: true, .Matched: false, .Text: "1<" }, |
| 160 | {.AllowComma: true, .Matched: false, .Text: "1>" }, |
| 161 | {.AllowComma: true, .Matched: false, .Text: "1<=" }, |
| 162 | {.AllowComma: true, .Matched: false, .Text: "1>=" }, |
| 163 | {.AllowComma: true, .Matched: false, .Text: "1==" }, |
| 164 | {.AllowComma: true, .Matched: false, .Text: "1!=" }, |
| 165 | {.AllowComma: true, .Matched: false, .Text: "1&" }, |
| 166 | {.AllowComma: true, .Matched: false, .Text: "1^" }, |
| 167 | {.AllowComma: true, .Matched: false, .Text: "1|" }, |
| 168 | {.AllowComma: true, .Matched: false, .Text: "1&&" }, |
| 169 | {.AllowComma: true, .Matched: false, .Text: "1||" }, |
| 170 | {.AllowComma: true, .Matched: false, .Text: "1," }, |
| 171 | {.AllowComma: true, .Matched: false, .Text: ",1" }, |
| 172 | {.AllowComma: true, .Matched: false, .Text: "1,1" }, |
| 173 | |
| 174 | // Accept valid ternary operators. |
| 175 | {.AllowComma: true, .Matched: true, .Text: "1?1:1" }, |
| 176 | {.AllowComma: true, .Matched: true, .Text: "1?:1" }, // A gcc extension treats x ? : y as x ? x : y. |
| 177 | // Reject invalid ternary operators. |
| 178 | {.AllowComma: true, .Matched: false, .Text: "?" }, |
| 179 | {.AllowComma: true, .Matched: false, .Text: "?1" }, |
| 180 | {.AllowComma: true, .Matched: false, .Text: "?:" }, |
| 181 | {.AllowComma: true, .Matched: false, .Text: "?:1" }, |
| 182 | {.AllowComma: true, .Matched: false, .Text: "?1:" }, |
| 183 | {.AllowComma: true, .Matched: false, .Text: "?1:1" }, |
| 184 | {.AllowComma: true, .Matched: false, .Text: "1?" }, |
| 185 | {.AllowComma: true, .Matched: false, .Text: "1?1" }, |
| 186 | {.AllowComma: true, .Matched: false, .Text: "1?:" }, |
| 187 | {.AllowComma: true, .Matched: false, .Text: "1?1:" }, |
| 188 | |
| 189 | // Accept parenthesized expressions. |
| 190 | {.AllowComma: true, .Matched: true, .Text: "(1)" }, |
| 191 | {.AllowComma: true, .Matched: true, .Text: "((+1))" }, |
| 192 | {.AllowComma: true, .Matched: true, .Text: "((+(1)))" }, |
| 193 | {.AllowComma: true, .Matched: true, .Text: "(-1)" }, |
| 194 | {.AllowComma: true, .Matched: true, .Text: "-(1)" }, |
| 195 | {.AllowComma: true, .Matched: true, .Text: "(+1)" }, |
| 196 | {.AllowComma: true, .Matched: true, .Text: "((+1))" }, |
| 197 | {.AllowComma: true, .Matched: true, .Text: "+(1)" }, |
| 198 | {.AllowComma: true, .Matched: true, .Text: "(~1)" }, |
| 199 | {.AllowComma: true, .Matched: true, .Text: "~(1)" }, |
| 200 | {.AllowComma: true, .Matched: true, .Text: "(!1)" }, |
| 201 | {.AllowComma: true, .Matched: true, .Text: "!(1)" }, |
| 202 | {.AllowComma: true, .Matched: true, .Text: "(1+1)" }, |
| 203 | {.AllowComma: true, .Matched: true, .Text: "(1-1)" }, |
| 204 | {.AllowComma: true, .Matched: true, .Text: "(1*1)" }, |
| 205 | {.AllowComma: true, .Matched: true, .Text: "(1/1)" }, |
| 206 | {.AllowComma: true, .Matched: true, .Text: "(1%2)" }, |
| 207 | {.AllowComma: true, .Matched: true, .Text: "(1<<1)" }, |
| 208 | {.AllowComma: true, .Matched: true, .Text: "(1>>1)" }, |
| 209 | {.AllowComma: true, .Matched: true, .Text: "(1<=>1)" }, |
| 210 | {.AllowComma: true, .Matched: true, .Text: "(1<1)" }, |
| 211 | {.AllowComma: true, .Matched: true, .Text: "(1>1)" }, |
| 212 | {.AllowComma: true, .Matched: true, .Text: "(1<=1)" }, |
| 213 | {.AllowComma: true, .Matched: true, .Text: "(1>=1)" }, |
| 214 | {.AllowComma: true, .Matched: true, .Text: "(1==1)" }, |
| 215 | {.AllowComma: true, .Matched: true, .Text: "(1!=1)" }, |
| 216 | {.AllowComma: true, .Matched: true, .Text: "(1&1)" }, |
| 217 | {.AllowComma: true, .Matched: true, .Text: "(1^1)" }, |
| 218 | {.AllowComma: true, .Matched: true, .Text: "(1|1)" }, |
| 219 | {.AllowComma: true, .Matched: true, .Text: "(1&&1)" }, |
| 220 | {.AllowComma: true, .Matched: true, .Text: "(1||1)" }, |
| 221 | {.AllowComma: true, .Matched: true, .Text: "(1?1:1)" }, |
| 222 | |
| 223 | // Accept more complicated "chained" expressions. |
| 224 | {.AllowComma: true, .Matched: true, .Text: "1+1+1" }, |
| 225 | {.AllowComma: true, .Matched: true, .Text: "1+1+1+1" }, |
| 226 | {.AllowComma: true, .Matched: true, .Text: "1+1+1+1+1" }, |
| 227 | {.AllowComma: true, .Matched: true, .Text: "1*1*1" }, |
| 228 | {.AllowComma: true, .Matched: true, .Text: "1*1*1*1" }, |
| 229 | {.AllowComma: true, .Matched: true, .Text: "1*1*1*1*1" }, |
| 230 | {.AllowComma: true, .Matched: true, .Text: "1<<1<<1" }, |
| 231 | {.AllowComma: true, .Matched: true, .Text: "4U>>1>>1" }, |
| 232 | {.AllowComma: true, .Matched: true, .Text: "1<1<1" }, |
| 233 | {.AllowComma: true, .Matched: true, .Text: "1>1>1" }, |
| 234 | {.AllowComma: true, .Matched: true, .Text: "1<=1<=1" }, |
| 235 | {.AllowComma: true, .Matched: true, .Text: "1>=1>=1" }, |
| 236 | {.AllowComma: true, .Matched: true, .Text: "1==1==1" }, |
| 237 | {.AllowComma: true, .Matched: true, .Text: "1!=1!=1" }, |
| 238 | {.AllowComma: true, .Matched: true, .Text: "1&1&1" }, |
| 239 | {.AllowComma: true, .Matched: true, .Text: "1^1^1" }, |
| 240 | {.AllowComma: true, .Matched: true, .Text: "1|1|1" }, |
| 241 | {.AllowComma: true, .Matched: true, .Text: "1&&1&&1" }, |
| 242 | {.AllowComma: true, .Matched: true, .Text: "1||1||1" }, |
| 243 | {.AllowComma: true, .Matched: true, .Text: "(1,1,1)" }, |
| 244 | |
| 245 | // Optionally reject comma operator |
| 246 | {.AllowComma: false, .Matched: false, .Text: "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 | {.Size: modernize::LiteralSize::Int, .Text: "1" }, |
| 260 | {.Size: modernize::LiteralSize::UnsignedInt, .Text: "1U" }, |
| 261 | {.Size: modernize::LiteralSize::Long, .Text: "1L" }, |
| 262 | {.Size: modernize::LiteralSize::UnsignedLong, .Text: "1UL" }, |
| 263 | {.Size: modernize::LiteralSize::UnsignedLong, .Text: "1LU" }, |
| 264 | {.Size: modernize::LiteralSize::LongLong, .Text: "1LL" }, |
| 265 | {.Size: modernize::LiteralSize::UnsignedLongLong, .Text: "1ULL" }, |
| 266 | {.Size: modernize::LiteralSize::UnsignedLongLong, .Text: "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 | |