diff --git a/include/clang/ASTMatchers/Dynamic/Diagnostics.h b/include/clang/ASTMatchers/Dynamic/Diagnostics.h index 2c76ddaa07d9e8380377f9d6edf0d40feace6fc8..908fa0db622d01b59fbb95461e9e5f811be18ad7 100644 --- a/include/clang/ASTMatchers/Dynamic/Diagnostics.h +++ b/include/clang/ASTMatchers/Dynamic/Diagnostics.h @@ -76,7 +76,7 @@ public: ET_ParserInvalidToken = 106, ET_ParserMalformedBindExpr = 107, ET_ParserTrailingCode = 108, - ET_ParserUnsignedError = 109, + ET_ParserNumberError = 109, ET_ParserOverloadedType = 110 }; diff --git a/include/clang/ASTMatchers/Dynamic/Parser.h b/include/clang/ASTMatchers/Dynamic/Parser.h index cd3b30ab8420af799089f2a72f2d98990f3192f1..5ec4a9abf4bff1a2d8a6ed064d87ed21e9dc9b8e 100644 --- a/include/clang/ASTMatchers/Dynamic/Parser.h +++ b/include/clang/ASTMatchers/Dynamic/Parser.h @@ -19,9 +19,10 @@ /// \code /// Grammar for the expressions supported: /// <Expression> := <Literal> | <NamedValue> | <MatcherExpression> -/// <Literal> := <StringLiteral> | <Boolean> | <Unsigned> +/// <Literal> := <StringLiteral> | <Boolean> | <Double> | <Unsigned> /// <StringLiteral> := "quoted string" /// <Boolean> := true | false +/// <Double> := [0-9]+.[0-9]* | [0-9]+.[0-9]*[eE][-+]?[0-9]+ /// <Unsigned> := [0-9]+ /// <NamedValue> := <Identifier> /// <MatcherExpression> := <Identifier>(<ArgumentList>) | diff --git a/include/clang/ASTMatchers/Dynamic/VariantValue.h b/include/clang/ASTMatchers/Dynamic/VariantValue.h index ce00bf7fe27fc640e4aee44ecdf90726cec06637..f9efe0f16f43738a48285e56344a6d9a77902d35 100644 --- a/include/clang/ASTMatchers/Dynamic/VariantValue.h +++ b/include/clang/ASTMatchers/Dynamic/VariantValue.h @@ -36,6 +36,7 @@ class ArgKind { enum Kind { AK_Matcher, AK_Boolean, + AK_Double, AK_Unsigned, AK_String }; @@ -243,6 +244,7 @@ struct VariantMatcher::TypedMatcherOps final : VariantMatcher::MatcherOps { /// /// Supported types: /// - \c bool +// - \c double /// - \c unsigned /// - \c llvm::StringRef /// - \c VariantMatcher (\c DynTypedMatcher / \c Matcher<T>) @@ -256,6 +258,7 @@ public: /// \brief Specific constructors for each supported type. VariantValue(bool Boolean); + VariantValue(double Double); VariantValue(unsigned Unsigned); VariantValue(StringRef String); VariantValue(const VariantMatcher &Matchers); @@ -272,6 +275,11 @@ public: bool getBoolean() const; void setBoolean(bool Boolean); + /// \brief Double value functions. + bool isDouble() const; + double getDouble() const; + void setDouble(double Double); + /// \brief Unsigned value functions. bool isUnsigned() const; unsigned getUnsigned() const; @@ -315,6 +323,7 @@ private: enum ValueType { VT_Nothing, VT_Boolean, + VT_Double, VT_Unsigned, VT_String, VT_Matcher @@ -323,6 +332,7 @@ private: /// \brief All supported value types. union AllValues { unsigned Unsigned; + double Double; bool Boolean; std::string *String; VariantMatcher *Matcher; diff --git a/lib/ASTMatchers/Dynamic/Diagnostics.cpp b/lib/ASTMatchers/Dynamic/Diagnostics.cpp index 787b780c4243ac22755e03ac20c4351244170b67..9cddcf93caef5ba3639bc78916c789b6d4b3be8c 100644 --- a/lib/ASTMatchers/Dynamic/Diagnostics.cpp +++ b/lib/ASTMatchers/Dynamic/Diagnostics.cpp @@ -118,8 +118,8 @@ static StringRef errorTypeToFormatString(Diagnostics::ErrorType Type) { return "Malformed bind() expression."; case Diagnostics::ET_ParserTrailingCode: return "Expected end of code."; - case Diagnostics::ET_ParserUnsignedError: - return "Error parsing unsigned token: <$0>"; + case Diagnostics::ET_ParserNumberError: + return "Error parsing numeric literal: <$0>"; case Diagnostics::ET_ParserOverloadedType: return "Input value has unresolved overloaded type: $0"; diff --git a/lib/ASTMatchers/Dynamic/Marshallers.h b/lib/ASTMatchers/Dynamic/Marshallers.h index 1d4ba037fde5feb00c127e6928c05fbd6e567d27..c557ff162691a338d69ff75159c3d54c1ec70e7a 100644 --- a/lib/ASTMatchers/Dynamic/Marshallers.h +++ b/lib/ASTMatchers/Dynamic/Marshallers.h @@ -75,6 +75,16 @@ template <> struct ArgTypeTraits<bool> { } }; +template <> struct ArgTypeTraits<double> { + static bool is(const VariantValue &Value) { return Value.isDouble(); } + static double get(const VariantValue &Value) { + return Value.getDouble(); + } + static ArgKind getKind() { + return ArgKind(ArgKind::AK_Double); + } +}; + template <> struct ArgTypeTraits<unsigned> { static bool is(const VariantValue &Value) { return Value.isUnsigned(); } static unsigned get(const VariantValue &Value) { diff --git a/lib/ASTMatchers/Dynamic/Parser.cpp b/lib/ASTMatchers/Dynamic/Parser.cpp index 967da8ac322f837f0d035b228a23a18e5618e1a7..ff5c5fb657c31f4176eb343add6c8f34753b122e 100644 --- a/lib/ASTMatchers/Dynamic/Parser.cpp +++ b/lib/ASTMatchers/Dynamic/Parser.cpp @@ -130,8 +130,8 @@ private: case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - // Parse an unsigned literal. - consumeUnsignedLiteral(&Result); + // Parse an unsigned and float literal. + consumeNumberLiteral(&Result); break; default: @@ -176,8 +176,9 @@ private: return Result; } - /// \brief Consume an unsigned literal. - void consumeUnsignedLiteral(TokenInfo *Result) { + /// \brief Consume an unsigned and float literal. + void consumeNumberLiteral(TokenInfo *Result) { + bool isFloatingLiteral = false; unsigned Length = 1; if (Code.size() > 1) { // Consume the 'x' or 'b' radix modifier, if present. @@ -188,20 +189,43 @@ private: while (Length < Code.size() && isHexDigit(Code[Length])) ++Length; + // Try to recognize a floating point literal. + while (Length < Code.size()) { + char c = Code[Length]; + if (c == '-' || c == '+' || c == '.' || isHexDigit(c)) { + isFloatingLiteral = true; + Length++; + } else { + break; + } + } + Result->Text = Code.substr(0, Length); Code = Code.drop_front(Length); - unsigned Value; - if (!Result->Text.getAsInteger(0, Value)) { - Result->Kind = TokenInfo::TK_Literal; - Result->Value = Value; + if (isFloatingLiteral) { + char *end; + errno = 0; + double doubleValue = strtod(Result->Text.str().c_str(), &end); + if (*end == 0 && errno == 0) { + Result->Kind = TokenInfo::TK_Literal; + Result->Value = doubleValue; + return; + } } else { - SourceRange Range; - Range.Start = Result->Range.Start; - Range.End = currentLocation(); - Error->addError(Range, Error->ET_ParserUnsignedError) << Result->Text; - Result->Kind = TokenInfo::TK_Error; + unsigned Value; + if (!Result->Text.getAsInteger(0, Value)) { + Result->Kind = TokenInfo::TK_Literal; + Result->Value = Value; + return; + } } + + SourceRange Range; + Range.Start = Result->Range.Start; + Range.End = currentLocation(); + Error->addError(Range, Error->ET_ParserNumberError) << Result->Text; + Result->Kind = TokenInfo::TK_Error; } /// \brief Consume a string literal. diff --git a/lib/ASTMatchers/Dynamic/VariantValue.cpp b/lib/ASTMatchers/Dynamic/VariantValue.cpp index a889e46fd6a138b11f17f93be2271bacf756cfea..57858d00acb45c579267131e88789b07f4b7e80a 100644 --- a/lib/ASTMatchers/Dynamic/VariantValue.cpp +++ b/lib/ASTMatchers/Dynamic/VariantValue.cpp @@ -26,6 +26,8 @@ std::string ArgKind::asString() const { return (Twine("Matcher<") + MatcherKind.asStringRef() + ">").str(); case AK_Boolean: return "boolean"; + case AK_Double: + return "double"; case AK_Unsigned: return "unsigned"; case AK_String: @@ -253,6 +255,10 @@ VariantValue::VariantValue(bool Boolean) : Type(VT_Nothing) { setBoolean(Boolean); } +VariantValue::VariantValue(double Double) : Type(VT_Nothing) { + setDouble(Double); +} + VariantValue::VariantValue(unsigned Unsigned) : Type(VT_Nothing) { setUnsigned(Unsigned); } @@ -274,6 +280,9 @@ VariantValue &VariantValue::operator=(const VariantValue &Other) { case VT_Boolean: setBoolean(Other.getBoolean()); break; + case VT_Double: + setDouble(Other.getDouble()); + break; case VT_Unsigned: setUnsigned(Other.getUnsigned()); break; @@ -300,6 +309,7 @@ void VariantValue::reset() { break; // Cases that do nothing. case VT_Boolean: + case VT_Double: case VT_Unsigned: case VT_Nothing: break; @@ -322,6 +332,21 @@ void VariantValue::setBoolean(bool NewValue) { Value.Boolean = NewValue; } +bool VariantValue::isDouble() const { + return Type == VT_Double; +} + +double VariantValue::getDouble() const { + assert(isDouble()); + return Value.Double; +} + +void VariantValue::setDouble(double NewValue) { + reset(); + Type = VT_Double; + Value.Double = NewValue; +} + bool VariantValue::isUnsigned() const { return Type == VT_Unsigned; } @@ -375,6 +400,12 @@ bool VariantValue::isConvertibleTo(ArgKind Kind, unsigned *Specificity) const { *Specificity = 1; return true; + case ArgKind::AK_Double: + if (!isDouble()) + return false; + *Specificity = 1; + return true; + case ArgKind::AK_Unsigned: if (!isUnsigned()) return false; @@ -415,6 +446,7 @@ std::string VariantValue::getTypeAsString() const { case VT_String: return "String"; case VT_Matcher: return getMatcher().getTypeAsString(); case VT_Boolean: return "Boolean"; + case VT_Double: return "Double"; case VT_Unsigned: return "Unsigned"; case VT_Nothing: return "Nothing"; } diff --git a/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/unittests/ASTMatchers/Dynamic/ParserTest.cpp index 504724362a789be81bf2eb4a1a3ee91e0e17d35e..ed184a8c149771a0240b35262d3654063619ce13 100644 --- a/unittests/ASTMatchers/Dynamic/ParserTest.cpp +++ b/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -84,6 +84,21 @@ TEST(ParserTest, ParseBoolean) { EXPECT_EQ(false, Sema.Values[1].getBoolean()); } +TEST(ParserTest, ParseDouble) { + MockSema Sema; + Sema.parse("1.0"); + Sema.parse("2.0f"); + Sema.parse("34.56e-78"); + Sema.parse("4.E+6"); + Sema.parse("1"); + EXPECT_EQ(5U, Sema.Values.size()); + EXPECT_EQ(1.0, Sema.Values[0].getDouble()); + EXPECT_EQ("1:1: Error parsing numeric literal: <2.0f>", Sema.Errors[1]); + EXPECT_EQ(34.56e-78, Sema.Values[2].getDouble()); + EXPECT_EQ(4e+6, Sema.Values[3].getDouble()); + EXPECT_FALSE(Sema.Values[4].isDouble()); +} + TEST(ParserTest, ParseUnsigned) { MockSema Sema; Sema.parse("0"); @@ -95,8 +110,8 @@ TEST(ParserTest, ParseUnsigned) { EXPECT_EQ(0U, Sema.Values[0].getUnsigned()); EXPECT_EQ(123U, Sema.Values[1].getUnsigned()); EXPECT_EQ(31U, Sema.Values[2].getUnsigned()); - EXPECT_EQ("1:1: Error parsing unsigned token: <12345678901>", Sema.Errors[3]); - EXPECT_EQ("1:1: Error parsing unsigned token: <1a1>", Sema.Errors[4]); + EXPECT_EQ("1:1: Error parsing numeric literal: <12345678901>", Sema.Errors[3]); + EXPECT_EQ("1:1: Error parsing numeric literal: <1a1>", Sema.Errors[4]); } TEST(ParserTest, ParseString) { diff --git a/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp index ca0eb9028584f813642b6c5f4c8f9aec0c00e888..7d3a07028a1ba4cbe82d0c08953f871f26faa786 100644 --- a/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp +++ b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp @@ -76,6 +76,7 @@ TEST(VariantValueTest, Assignment) { EXPECT_EQ("A", Value.getString()); EXPECT_TRUE(Value.hasValue()); EXPECT_FALSE(Value.isBoolean()); + EXPECT_FALSE(Value.isDouble()); EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isMatcher()); EXPECT_EQ("String", Value.getTypeAsString()); @@ -83,6 +84,7 @@ TEST(VariantValueTest, Assignment) { Value = VariantMatcher::SingleMatcher(recordDecl()); EXPECT_TRUE(Value.hasValue()); EXPECT_FALSE(Value.isBoolean()); + EXPECT_FALSE(Value.isDouble()); EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isString()); EXPECT_TRUE(Value.isMatcher()); @@ -98,10 +100,20 @@ TEST(VariantValueTest, Assignment) { EXPECT_FALSE(Value.isMatcher()); EXPECT_FALSE(Value.isString()); + Value = 3.14; + EXPECT_TRUE(Value.isDouble()); + EXPECT_EQ(3.14, Value.getDouble()); + EXPECT_TRUE(Value.hasValue()); + EXPECT_FALSE(Value.isBoolean()); + EXPECT_FALSE(Value.isUnsigned()); + EXPECT_FALSE(Value.isMatcher()); + EXPECT_FALSE(Value.isString()); + Value = 17; EXPECT_TRUE(Value.isUnsigned()); EXPECT_EQ(17U, Value.getUnsigned()); EXPECT_FALSE(Value.isBoolean()); + EXPECT_FALSE(Value.isDouble()); EXPECT_TRUE(Value.hasValue()); EXPECT_FALSE(Value.isMatcher()); EXPECT_FALSE(Value.isString()); @@ -109,6 +121,7 @@ TEST(VariantValueTest, Assignment) { Value = VariantValue(); EXPECT_FALSE(Value.hasValue()); EXPECT_FALSE(Value.isBoolean()); + EXPECT_FALSE(Value.isDouble()); EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isString()); EXPECT_FALSE(Value.isMatcher());