#include "IntegralLiteralExpressionMatcher.h"
+#include <algorithm>
#include <cctype>
#include <stdexcept>
return true;
}
+static LiteralSize literalTokenSize(const Token &Tok) {
+ unsigned int Length = Tok.getLength();
+ if (Length <= 1)
+ return LiteralSize::Int;
+
+ bool SeenUnsigned = false;
+ bool SeenLong = false;
+ bool SeenLongLong = false;
+ const char *Text = Tok.getLiteralData();
+ for (unsigned int End = Length - 1; End > 0; --End) {
+ if (std::isdigit(Text[End]))
+ break;
+
+ if (std::toupper(Text[End]) == 'U')
+ SeenUnsigned = true;
+ else if (std::toupper(Text[End]) == 'L') {
+ if (SeenLong)
+ SeenLongLong = true;
+ SeenLong = true;
+ }
+ }
+
+ if (SeenLongLong) {
+ if (SeenUnsigned)
+ return LiteralSize::UnsignedLongLong;
+
+ return LiteralSize::LongLong;
+ }
+ if (SeenLong) {
+ if (SeenUnsigned)
+ return LiteralSize::UnsignedLong;
+
+ return LiteralSize::Long;
+ }
+ if (SeenUnsigned)
+ return LiteralSize::UnsignedInt;
+
+ return LiteralSize::Int;
+}
+
+static bool operator<(LiteralSize LHS, LiteralSize RHS) {
+ return static_cast<int>(LHS) < static_cast<int>(RHS);
+}
+
bool IntegralLiteralExpressionMatcher::unaryExpr() {
if (!unaryOperator())
return false;
!isIntegralConstant(*Current)) {
return false;
}
+
+ LargestSize = std::max(LargestSize, literalTokenSize(*Current));
++Current;
+
return true;
}
}
bool IntegralLiteralExpressionMatcher::commaExpr() {
- return nonTerminalChainedExpr<tok::TokenKind::comma>(
- &IntegralLiteralExpressionMatcher::conditionalExpr);
+ auto Pred = CommaAllowed
+ ? std::function<bool(Token)>(
+ [](Token Tok) { return Tok.is(tok::TokenKind::comma); })
+ : std::function<bool(Token)>([](Token) { return false; });
+ return nonTerminalChainedExpr(
+ &IntegralLiteralExpressionMatcher::conditionalExpr, Pred);
}
bool IntegralLiteralExpressionMatcher::expr() { return commaExpr(); }
bool IntegralLiteralExpressionMatcher::match() {
- return expr() && Current == End;
+ // Top-level allowed expression is conditionalExpr(), not expr(), because
+ // comma operators are only valid initializers when used inside parentheses.
+ return conditionalExpr() && Current == End;
+}
+
+LiteralSize IntegralLiteralExpressionMatcher::largestLiteralSize() const {
+ return LargestSize;
}
} // namespace modernize
return Tokens;
}
-static bool matchText(const char *Text) {
+static bool matchText(const char *Text, bool AllowComma) {
std::vector<Token> Tokens{tokenify(Text)};
- modernize::IntegralLiteralExpressionMatcher Matcher(Tokens);
+ 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 Param {
+struct MatchParam {
+ bool AllowComma;
bool Matched;
const char *Text;
- friend std::ostream &operator<<(std::ostream &Str, const Param &Value) {
- return Str << "Matched: " << std::boolalpha << Value.Matched << ", Text: '"
- << Value.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 << '\'';
}
};
-class MatcherTest : public ::testing::TestWithParam<Param> {};
+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 Param Params[] = {
+static const MatchParam MatchParams[] = {
// Accept integral literals.
- {true, "1"},
- {true, "0177"},
- {true, "0xdeadbeef"},
- {true, "0b1011"},
- {true, "'c'"},
+ {true, true, "1"},
+ {true, true, "0177"},
+ {true, true, "0xdeadbeef"},
+ {true, true, "0b1011"},
+ {true, true, "'c'"},
// Reject non-integral literals.
- {false, "1.23"},
- {false, "0x1p3"},
- {false, R"("string")"},
- {false, "1i"},
+ {true, false, "1.23"},
+ {true, false, "0x1p3"},
+ {true, false, R"("string")"},
+ {true, false, "1i"},
// Accept literals with these unary operators.
- {true, "-1"},
- {true, "+1"},
- {true, "~1"},
- {true, "!1"},
+ {true, true, "-1"},
+ {true, true, "+1"},
+ {true, true, "~1"},
+ {true, true, "!1"},
// Reject invalid unary operators.
- {false, "1-"},
- {false, "1+"},
- {false, "1~"},
- {false, "1!"},
+ {true, false, "1-"},
+ {true, false, "1+"},
+ {true, false, "1~"},
+ {true, false, "1!"},
// Accept valid binary operators.
- {true, "1+1"},
- {true, "1-1"},
- {true, "1*1"},
- {true, "1/1"},
- {true, "1%2"},
- {true, "1<<1"},
- {true, "1>>1"},
- {true, "1<=>1"},
- {true, "1<1"},
- {true, "1>1"},
- {true, "1<=1"},
- {true, "1>=1"},
- {true, "1==1"},
- {true, "1!=1"},
- {true, "1&1"},
- {true, "1^1"},
- {true, "1|1"},
- {true, "1&&1"},
- {true, "1||1"},
- {true, "1+ +1"}, // A space is needed to avoid being tokenized as ++ or --.
- {true, "1- -1"},
- {true, "1,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"}, // 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.
- {false, "1+"},
- {false, "1-"},
- {false, "1*"},
- {false, "1/"},
- {false, "1%"},
- {false, "1<<"},
- {false, "1>>"},
- {false, "1<=>"},
- {false, "1<"},
- {false, "1>"},
- {false, "1<="},
- {false, "1>="},
- {false, "1=="},
- {false, "1!="},
- {false, "1&"},
- {false, "1^"},
- {false, "1|"},
- {false, "1&&"},
- {false, "1||"},
- {false, "1,"},
- {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"},
+ {true, false, "1,1"},
// Accept valid ternary operators.
- {true, "1?1:1"},
- {true, "1?:1"}, // A gcc extension treats x ? : y as x ? x : y.
+ {true, true, "1?1:1"},
+ {true, true, "1?:1"}, // A gcc extension treats x ? : y as x ? x : y.
// Reject invalid ternary operators.
- {false, "?"},
- {false, "?1"},
- {false, "?:"},
- {false, "?:1"},
- {false, "?1:"},
- {false, "?1:1"},
- {false, "1?"},
- {false, "1?1"},
- {false, "1?:"},
- {false, "1?1:"},
+ {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, "(1)"},
- {true, "((+1))"},
- {true, "((+(1)))"},
- {true, "(-1)"},
- {true, "-(1)"},
- {true, "(+1)"},
- {true, "((+1))"},
- {true, "+(1)"},
- {true, "(~1)"},
- {true, "~(1)"},
- {true, "(!1)"},
- {true, "!(1)"},
- {true, "(1+1)"},
- {true, "(1-1)"},
- {true, "(1*1)"},
- {true, "(1/1)"},
- {true, "(1%2)"},
- {true, "(1<<1)"},
- {true, "(1>>1)"},
- {true, "(1<=>1)"},
- {true, "(1<1)"},
- {true, "(1>1)"},
- {true, "(1<=1)"},
- {true, "(1>=1)"},
- {true, "(1==1)"},
- {true, "(1!=1)"},
- {true, "(1&1)"},
- {true, "(1^1)"},
- {true, "(1|1)"},
- {true, "(1&&1)"},
- {true, "(1||1)"},
- {true, "(1?1: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)"},
+ {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, "1+1+1"},
- {true, "1+1+1+1"},
- {true, "1+1+1+1+1"},
- {true, "1*1*1"},
- {true, "1*1*1*1"},
- {true, "1*1*1*1*1"},
- {true, "1<<1<<1"},
- {true, "4U>>1>>1"},
- {true, "1<1<1"},
- {true, "1>1>1"},
- {true, "1<=1<=1"},
- {true, "1>=1>=1"},
- {true, "1==1==1"},
- {true, "1!=1!=1"},
- {true, "1&1&1"},
- {true, "1^1^1"},
- {true, "1|1|1"},
- {true, "1&&1&&1"},
- {true, "1||1||1"},
- {true, "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, "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) {
- EXPECT_TRUE(matchText(GetParam().Text) == GetParam().Matched);
+ const MatchParam &Param = GetParam();
+
+ EXPECT_TRUE(matchText(Param.Text, Param.AllowComma) == Param.Matched);
}
-INSTANTIATE_TEST_SUITE_P(TokenExpressionParserTests, MatcherTest,
- ::testing::ValuesIn(Params));
+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