/// \brief Helper class to manage error messages.
class Diagnostics {
- public:
+public:
+ /// \brief Parser context types.
+ enum ContextType {
+ CT_MatcherArg = 0,
+ CT_MatcherConstruct = 1
+ };
+
/// \brief All errors from the system.
enum ErrorType {
ET_None = 0,
ET_RegistryNotBindable = 4,
ET_ParserStringError = 100,
- ET_ParserMatcherArgFailure = 101,
- ET_ParserMatcherFailure = 102,
- ET_ParserNoOpenParen = 103,
- ET_ParserNoCloseParen = 104,
- ET_ParserNoComma = 105,
- ET_ParserNoCode = 106,
- ET_ParserNotAMatcher = 107,
- ET_ParserInvalidToken = 108,
- ET_ParserMalformedBindExpr = 109,
- ET_ParserTrailingCode = 110,
- ET_ParserUnsignedError = 111,
- ET_ParserOverloadedType = 112
+ ET_ParserNoOpenParen = 101,
+ ET_ParserNoCloseParen = 102,
+ ET_ParserNoComma = 103,
+ ET_ParserNoCode = 104,
+ ET_ParserNotAMatcher = 105,
+ ET_ParserInvalidToken = 106,
+ ET_ParserMalformedBindExpr = 107,
+ ET_ParserTrailingCode = 108,
+ ET_ParserUnsignedError = 109,
+ ET_ParserOverloadedType = 110
};
/// \brief Helper stream class.
- struct ArgStream {
+ class ArgStream {
+ public:
+ ArgStream(std::vector<std::string> *Out) : Out(Out) {}
template <class T> ArgStream &operator<<(const T &Arg) {
return operator<<(Twine(Arg));
}
ArgStream &operator<<(const Twine &Arg);
+
+ private:
std::vector<std::string> *Out;
};
- /// \brief Push a frame to the beginning of the list
+ /// \brief Class defining a parser context.
+ ///
+ /// Used by the parser to specify (possibly recursive) contexts where the
+ /// parsing/construction can fail. Any error triggered within a context will
+ /// keep information about the context chain.
+ /// This class should be used as a RAII instance in the stack.
+ struct Context {
+ public:
+ /// \brief About to call the constructor for a matcher.
+ enum ConstructMatcherEnum { ConstructMatcher };
+ Context(ConstructMatcherEnum, Diagnostics *Error, StringRef MatcherName,
+ const SourceRange &MatcherRange);
+ /// \brief About to recurse into parsing one argument for a matcher.
+ enum MatcherArgEnum { MatcherArg };
+ Context(MatcherArgEnum, Diagnostics *Error, StringRef MatcherName,
+ const SourceRange &MatcherRange, unsigned ArgNumber);
+ ~Context();
+
+ private:
+ Diagnostics *const Error;
+ };
+
+ /// \brief Add an error to the diagnostics.
///
- /// Returns a helper class to allow the caller to pass the arguments for the
+ /// All the context information will be kept on the error message.
+ /// \return a helper class to allow the caller to pass the arguments for the
/// error message, using the << operator.
- ArgStream pushErrorFrame(const SourceRange &Range, ErrorType Error);
+ ArgStream addError(const SourceRange &Range, ErrorType Error);
+
+ /// \brief Information stored for one frame of the context.
+ struct ContextFrame {
+ ContextType Type;
+ SourceRange Range;
+ std::vector<std::string> Args;
+
+ std::string ToString() const;
+ };
- struct ErrorFrame {
+ /// \brief Information stored for each error found.
+ struct ErrorContent {
+ std::vector<ContextFrame> ContextStack;
SourceRange Range;
ErrorType Type;
std::vector<std::string> Args;
std::string ToString() const;
};
- ArrayRef<ErrorFrame> frames() const { return Frames; }
+ ArrayRef<ErrorContent> errors() const { return Errors; }
- /// \brief Returns a string representation of the last frame.
+ /// \brief Returns a simple string representation of each error.
+ ///
+ /// Each error only shows the error message without any context.
std::string ToString() const;
- /// \brief Returns a string representation of the whole frame stack.
+
+ /// \brief Returns the full string representation of each error.
+ ///
+ /// Each error message contains the full context.
std::string ToStringFull() const;
- private:
- std::vector<ErrorFrame> Frames;
+private:
+ /// \brief Helper function used by the constructors of ContextFrame.
+ ArgStream pushContextFrame(ContextType Type, SourceRange Range);
+
+ std::vector<ContextFrame> ContextStack;
+ std::vector<ErrorContent> Errors;
};
} // namespace dynamic
namespace ast_matchers {
namespace dynamic {
-Diagnostics::ArgStream &
-Diagnostics::ArgStream::operator<<(const Twine &Arg) {
+Diagnostics::ArgStream Diagnostics::pushContextFrame(ContextType Type,
+ SourceRange Range) {
+ ContextStack.push_back(ContextFrame());
+ ContextFrame& data = ContextStack.back();
+ data.Type = Type;
+ data.Range = Range;
+ return ArgStream(&data.Args);
+}
+
+Diagnostics::Context::Context(ConstructMatcherEnum, Diagnostics *Error,
+ StringRef MatcherName,
+ const SourceRange &MatcherRange)
+ : Error(Error) {
+ Error->pushContextFrame(CT_MatcherConstruct, MatcherRange) << MatcherName;
+}
+
+Diagnostics::Context::Context(MatcherArgEnum, Diagnostics *Error,
+ StringRef MatcherName,
+ const SourceRange &MatcherRange,
+ unsigned ArgNumber)
+ : Error(Error) {
+ Error->pushContextFrame(CT_MatcherArg, MatcherRange) << ArgNumber
+ << MatcherName;
+}
+
+Diagnostics::Context::~Context() { Error->ContextStack.pop_back(); }
+
+Diagnostics::ArgStream &Diagnostics::ArgStream::operator<<(const Twine &Arg) {
Out->push_back(Arg.str());
return *this;
}
-Diagnostics::ArgStream Diagnostics::pushErrorFrame(const SourceRange &Range,
- ErrorType Error) {
- Frames.insert(Frames.begin(), ErrorFrame());
- ErrorFrame &Last = Frames.front();
+Diagnostics::ArgStream Diagnostics::addError(const SourceRange &Range,
+ ErrorType Error) {
+ Errors.push_back(ErrorContent());
+ ErrorContent &Last = Errors.back();
+ Last.ContextStack = ContextStack;
Last.Range = Range;
Last.Type = Error;
- ArgStream Out = { &Last.Args };
- return Out;
+ return ArgStream(&Last.Args);
+}
+
+StringRef ContextTypeToString(Diagnostics::ContextType Type) {
+ switch (Type) {
+ case Diagnostics::CT_MatcherConstruct:
+ return "Error building matcher $0.";
+ case Diagnostics::CT_MatcherArg:
+ return "Error parsing argument $0 for matcher $1.";
+ }
+ llvm_unreachable("Unknown ContextType value.");
}
StringRef ErrorTypeToString(Diagnostics::ErrorType Type) {
case Diagnostics::ET_ParserStringError:
return "Error parsing string token: <$0>";
- case Diagnostics::ET_ParserMatcherArgFailure:
- return "Error parsing argument $0 for matcher $1.";
- case Diagnostics::ET_ParserMatcherFailure:
- return "Error building matcher $0.";
case Diagnostics::ET_ParserNoOpenParen:
return "Error parsing matcher. Found token <$0> while looking for '('.";
case Diagnostics::ET_ParserNoCloseParen:
return Out;
}
-std::string Diagnostics::ErrorFrame::ToString() const {
- StringRef FormatString = ErrorTypeToString(Type);
- std::string ErrorOut = FormatErrorString(FormatString, Args);
+static std::string MaybeAddLineAndColumn(Twine Input,
+ const SourceRange &Range) {
if (Range.Start.Line > 0 && Range.Start.Column > 0)
return (Twine(Range.Start.Line) + ":" + Twine(Range.Start.Column) + ": " +
- ErrorOut).str();
- return ErrorOut;
+ Input).str();
+ return Input.str();
+}
+
+std::string Diagnostics::ContextFrame::ToString() const {
+ return MaybeAddLineAndColumn(
+ FormatErrorString(ContextTypeToString(Type), Args), Range);
+}
+
+std::string Diagnostics::ErrorContent::ToString() const {
+ return MaybeAddLineAndColumn(FormatErrorString(ErrorTypeToString(Type), Args),
+ Range);
}
std::string Diagnostics::ToString() const {
- if (Frames.empty()) return "";
- return Frames[Frames.size() - 1].ToString();
+ std::string Result;
+ for (size_t i = 0, e = Errors.size(); i != e; ++i) {
+ if (i != 0) Result += "\n";
+ Result += Errors[i].ToString();
+ }
+ return Result;
}
std::string Diagnostics::ToStringFull() const {
std::string Result;
- for (size_t i = 0, end = Frames.size(); i != end; ++i) {
- if (i > 0) Result += "\n";
- Result += Frames[i].ToString();
+ for (size_t i = 0, e = Errors.size(); i != e; ++i) {
+ if (i != 0) Result += "\n";
+ const ErrorContent &Error = Errors[i];
+ for (size_t i = 0, e = Error.ContextStack.size(); i != e; ++i) {
+ Result += Error.ContextStack[i].ToString() + "\n";
+ }
+ Result += Error.ToString();
}
return Result;
}
/// \brief Helper macros to check the arguments on all marshaller functions.
#define CHECK_ARG_COUNT(count) \
if (Args.size() != count) { \
- Error->pushErrorFrame(NameRange, Error->ET_RegistryWrongArgCount) \
+ Error->addError(NameRange, Error->ET_RegistryWrongArgCount) \
<< count << Args.size(); \
return MatcherList(); \
}
#define CHECK_ARG_TYPE(index, type) \
if (!ArgTypeTraits<type>::is(Args[index].Value)) { \
- Error->pushErrorFrame(Args[index].Range, Error->ET_RegistryWrongArgType) \
+ Error->addError(Args[index].Range, Error->ET_RegistryWrongArgType) \
<< (index + 1) << ArgTypeTraits<type>::asString() \
<< Args[index].Value.getTypeAsString(); \
return MatcherList(); \
const ParserValue &Arg = Args[i];
const VariantValue &Value = Arg.Value;
if (!ArgTraits::is(Value)) {
- Error->pushErrorFrame(Arg.Range, Error->ET_RegistryWrongArgType)
+ Error->addError(Arg.Range, Error->ET_RegistryWrongArgType)
<< (i + 1) << ArgTraits::asString() << Value.getTypeAsString();
HasError = true;
break;
SourceRange Range;
Range.Start = Result->Range.Start;
Range.End = currentLocation();
- Error->pushErrorFrame(Range, Error->ET_ParserUnsignedError)
- << Result->Text;
+ Error->addError(Range, Error->ET_ParserUnsignedError) << Result->Text;
Result->Kind = TokenInfo::TK_Error;
}
}
SourceRange Range;
Range.Start = Result->Range.Start;
Range.End = currentLocation();
- Error->pushErrorFrame(Range, Error->ET_ParserStringError)
- << ErrorText;
+ Error->addError(Range, Error->ET_ParserStringError) << ErrorText;
Result->Kind = TokenInfo::TK_Error;
}
assert(NameToken.Kind == TokenInfo::TK_Ident);
const TokenInfo OpenToken = Tokenizer->consumeNextToken();
if (OpenToken.Kind != TokenInfo::TK_OpenParen) {
- Error->pushErrorFrame(OpenToken.Range, Error->ET_ParserNoOpenParen)
+ Error->addError(OpenToken.Range, Error->ET_ParserNoOpenParen)
<< OpenToken.Text;
return false;
}
// We must find a , token to continue.
const TokenInfo CommaToken = Tokenizer->consumeNextToken();
if (CommaToken.Kind != TokenInfo::TK_Comma) {
- Error->pushErrorFrame(CommaToken.Range, Error->ET_ParserNoComma)
+ Error->addError(CommaToken.Range, Error->ET_ParserNoComma)
<< CommaToken.Text;
return false;
}
}
+ Diagnostics::Context Ctx(Diagnostics::Context::MatcherArg, Error,
+ NameToken.Text, NameToken.Range, Args.size() + 1);
ParserValue ArgValue;
ArgValue.Text = Tokenizer->peekNextToken().Text;
ArgValue.Range = Tokenizer->peekNextToken().Range;
- if (!parseExpressionImpl(&ArgValue.Value)) {
- Error->pushErrorFrame(NameToken.Range,
- Error->ET_ParserMatcherArgFailure)
- << (Args.size() + 1) << NameToken.Text;
- return false;
- }
+ if (!parseExpressionImpl(&ArgValue.Value)) return false;
Args.push_back(ArgValue);
}
if (EndToken.Kind == TokenInfo::TK_Eof) {
- Error->pushErrorFrame(OpenToken.Range, Error->ET_ParserNoCloseParen);
+ Error->addError(OpenToken.Range, Error->ET_ParserNoCloseParen);
return false;
}
// explicit about the syntax error.
if (BindToken.Kind != TokenInfo::TK_Ident ||
BindToken.Text != TokenInfo::ID_Bind) {
- Error->pushErrorFrame(BindToken.Range, Error->ET_ParserMalformedBindExpr);
+ Error->addError(BindToken.Range, Error->ET_ParserMalformedBindExpr);
return false;
}
if (OpenToken.Kind != TokenInfo::TK_OpenParen) {
- Error->pushErrorFrame(OpenToken.Range, Error->ET_ParserMalformedBindExpr);
+ Error->addError(OpenToken.Range, Error->ET_ParserMalformedBindExpr);
return false;
}
if (IDToken.Kind != TokenInfo::TK_Literal || !IDToken.Value.isString()) {
- Error->pushErrorFrame(IDToken.Range, Error->ET_ParserMalformedBindExpr);
+ Error->addError(IDToken.Range, Error->ET_ParserMalformedBindExpr);
return false;
}
if (CloseToken.Kind != TokenInfo::TK_CloseParen) {
- Error->pushErrorFrame(CloseToken.Range,
- Error->ET_ParserMalformedBindExpr);
+ Error->addError(CloseToken.Range, Error->ET_ParserMalformedBindExpr);
return false;
}
BindID = IDToken.Value.getString();
}
// Merge the start and end infos.
+ Diagnostics::Context Ctx(Diagnostics::Context::ConstructMatcher, Error,
+ NameToken.Text, NameToken.Range);
SourceRange MatcherRange = NameToken.Range;
MatcherRange.End = EndToken.Range.End;
MatcherList Result = S->actOnMatcherExpression(
NameToken.Text, MatcherRange, BindID, Args, Error);
- if (Result.empty()) {
- Error->pushErrorFrame(NameToken.Range, Error->ET_ParserMatcherFailure)
- << NameToken.Text;
- return false;
- }
+ if (Result.empty()) return false;
*Value = Result;
return true;
return parseMatcherExpressionImpl(Value);
case TokenInfo::TK_Eof:
- Error->pushErrorFrame(Tokenizer->consumeNextToken().Range,
- Error->ET_ParserNoCode);
+ Error->addError(Tokenizer->consumeNextToken().Range,
+ Error->ET_ParserNoCode);
return false;
case TokenInfo::TK_Error:
case TokenInfo::TK_Period:
case TokenInfo::TK_InvalidChar:
const TokenInfo Token = Tokenizer->consumeNextToken();
- Error->pushErrorFrame(Token.Range, Error->ET_ParserInvalidToken)
- << Token.Text;
+ Error->addError(Token.Range, Error->ET_ParserInvalidToken) << Token.Text;
return false;
}
CodeTokenizer Tokenizer(Code, Error);
if (!Parser(&Tokenizer, S, Error).parseExpressionImpl(Value)) return false;
if (Tokenizer.peekNextToken().Kind != TokenInfo::TK_Eof) {
- Error->pushErrorFrame(Tokenizer.peekNextToken().Range,
- Error->ET_ParserTrailingCode);
+ Error->addError(Tokenizer.peekNextToken().Range,
+ Error->ET_ParserTrailingCode);
return false;
}
return true;
if (!parseExpression(Code, S, &Value, Error))
return NULL;
if (!Value.isMatchers()) {
- Error->pushErrorFrame(SourceRange(), Error->ET_ParserNotAMatcher);
+ Error->addError(SourceRange(), Error->ET_ParserNotAMatcher);
return NULL;
}
if (Value.getMatchers().matchers().size() != 1) {
- Error->pushErrorFrame(SourceRange(), Error->ET_ParserOverloadedType)
+ Error->addError(SourceRange(), Error->ET_ParserOverloadedType)
<< Value.getTypeAsString();
return NULL;
}
ConstructorMap::const_iterator it =
RegistryData->constructors().find(MatcherName);
if (it == RegistryData->constructors().end()) {
- Error->pushErrorFrame(NameRange, Error->ET_RegistryNotFound)
- << MatcherName;
+ Error->addError(NameRange, Error->ET_RegistryNotFound) << MatcherName;
return MatcherList();
}
return *Bound;
}
}
- Error->pushErrorFrame(NameRange, Error->ET_RegistryNotBindable);
+ Error->addError(NameRange, Error->ET_RegistryNotBindable);
return MatcherList();
}