Files
swift-mirror/include/swift/Parse/ParserResult.h
Nathan Hawes bb773232a8 [Parse][CodeCompletion] Stop code completion within a closure causing parser recovery after the closure.
For example, the completion below would trigger error recovery within the
closure, which we recover from by skipping to the first inner closure's right
brace. The fact that we recovered though, was not recorded. The closure is
treated as still being an error, triggering another recovery after it that
skips over the 'Thing' token, giving a lone closure expression, rather than a
call.

CreateThings {
    Thing { point in
      print("hello")
      point.#^HERE^#
    }
    Thing { _ in }
}

This isn't an issue for code completion when the outer closure is a regular
closure, but when it's a function builder, invalid elements result in no types
being applied (no valid solutions) and we end up with no completion results.

The fix here is removing the error status from the parser result after the
initial parser recovery.
2020-09-10 21:59:09 -07:00

251 lines
7.5 KiB
C++

//===--- ParserResult.h - Parser Result Wrapper -----------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_PARSER_PARSER_RESULT_H
#define SWIFT_PARSER_PARSER_RESULT_H
#include "llvm/ADT/PointerIntPair.h"
#include "swift/AST/ParameterList.h"
#include <type_traits>
namespace swift {
class ParserStatus;
/// A wrapper for a parser AST node result (Decl, Stmt, Expr, Pattern,
/// etc.)
///
/// Contains the pointer to the AST node itself (or null) and additional bits
/// that indicate:
/// \li if there was a parse error;
/// \li if there was a code completion token.
///
/// If you want to return an AST node pointer in the Parser, consider using
/// ParserResult instead.
template <typename T> class ParserResult {
llvm::PointerIntPair<T *, 2> PtrAndBits;
enum {
IsError = 0x1,
IsCodeCompletion = 0x2,
};
template <typename U>
friend class ParserResult;
template <typename U>
friend inline ParserResult<U> makeParserResult(ParserStatus Status,
U *Result);
public:
/// Construct a null result with error bit set.
ParserResult(std::nullptr_t = nullptr) { setIsParseError(); }
/// Construct a null result with specified error bits set.
ParserResult(ParserStatus Status);
/// Construct a successful parser result.
explicit ParserResult(T *Result) : PtrAndBits(Result) {
assert(Result && "a successful parser result cannot be null");
}
/// Convert from a different but compatible parser result.
template <typename U, typename Enabler = typename std::enable_if<
std::is_base_of<T, U>::value>::type>
ParserResult(ParserResult<U> Other)
: PtrAndBits(Other.PtrAndBits.getPointer(), Other.PtrAndBits.getInt()) {}
/// Return true if this result does not have an AST node.
///
/// If returns true, then error bit is set.
bool isNull() const { return getPtrOrNull() == nullptr; }
/// Return true if this result has an AST node.
///
/// Note that this does not tell us if there was a parse error or not.
bool isNonNull() const { return getPtrOrNull() != nullptr; }
/// Return the AST node if non-null.
T *get() const {
assert(getPtrOrNull() && "not checked for nullptr");
return getPtrOrNull();
}
/// Return the AST node or a null pointer.
T *getPtrOrNull() const { return PtrAndBits.getPointer(); }
/// Return true if there was a parse error that the parser has not yet
/// recovered from.
///
/// Note that we can still have an AST node which was constructed during
/// recovery.
bool isParseError() const { return PtrAndBits.getInt() & IsError; }
/// Return true if there was a parse error that the parser has not yet
/// recovered from, or if we found a code completion token while parsing.
///
/// Note that we can still have an AST node which was constructed during
/// recovery.
bool isParseErrorOrHasCompletion() const {
return PtrAndBits.getInt() & (IsError | IsCodeCompletion);
}
/// Return true if we found a code completion token while parsing this.
bool hasCodeCompletion() const {
return PtrAndBits.getInt() & IsCodeCompletion;
}
void setIsParseError() { PtrAndBits.setInt(PtrAndBits.getInt() | IsError); }
void setHasCodeCompletionAndIsError() {
PtrAndBits.setInt(PtrAndBits.getInt() | IsError | IsCodeCompletion);
}
private:
void setHasCodeCompletion() {
PtrAndBits.setInt(PtrAndBits.getInt() | IsCodeCompletion);
}
};
/// Create a successful parser result.
template <typename T>
static inline ParserResult<T> makeParserResult(T *Result) {
return ParserResult<T>(Result);
}
/// Create a result (null or non-null) with error bit set.
template <typename T>
static inline ParserResult<T> makeParserErrorResult(T *Result = nullptr) {
ParserResult<T> PR;
if (Result)
PR = ParserResult<T>(Result);
PR.setIsParseError();
return PR;
}
/// Create a result (null or non-null) with error and code completion bits set.
template <typename T>
static inline ParserResult<T> makeParserCodeCompletionResult(T *Result =
nullptr) {
ParserResult<T> PR;
if (Result)
PR = ParserResult<T>(Result);
PR.setHasCodeCompletionAndIsError();
return PR;
}
/// Same as \c ParserResult, but just the status bits without the AST
/// node.
///
/// Useful when the AST node is returned by some other means (for example, in
/// a vector out parameter).
///
/// If you want to use 'bool' as a result type in the Parser, consider using
/// ParserStatus instead.
class ParserStatus {
unsigned IsError : 1;
unsigned IsCodeCompletion : 1;
public:
/// Construct a successful parser status.
ParserStatus() : IsError(0), IsCodeCompletion(0) {}
/// Construct a parser status with specified bits.
template<typename T>
ParserStatus(ParserResult<T> Result) : IsError(0), IsCodeCompletion(0) {
if (Result.isParseError())
setIsParseError();
if (Result.hasCodeCompletion())
IsCodeCompletion = true;
}
/// Return true if either 1) no errors were encountered while parsing this,
/// or 2) there were errors but the the parser already recovered from them.
bool isSuccess() const { return !isError(); }
bool isErrorOrHasCompletion() const { return IsError || IsCodeCompletion; }
/// Return true if we found a code completion token while parsing this.
bool hasCodeCompletion() const { return IsCodeCompletion; }
/// Return true if we encountered any errors while parsing this that the
/// parser hasn't yet recovered from.
bool isError() const { return IsError; }
void setIsParseError() {
IsError = true;
}
void clearIsError() {
IsError = false;
}
void setHasCodeCompletionAndIsError() {
IsError = true;
IsCodeCompletion = true;
}
ParserStatus &operator|=(ParserStatus RHS) {
IsError |= RHS.IsError;
IsCodeCompletion |= RHS.IsCodeCompletion;
return *this;
}
friend ParserStatus operator|(ParserStatus LHS, ParserStatus RHS) {
ParserStatus Result = LHS;
Result |= RHS;
return Result;
}
};
/// Create a successful parser status.
static inline ParserStatus makeParserSuccess() {
return ParserStatus();
}
/// Create a status with error bit set.
static inline ParserStatus makeParserError() {
ParserStatus Status;
Status.setIsParseError();
return Status;
}
/// Create a status with error and code completion bits set.
static inline ParserStatus makeParserCodeCompletionStatus() {
ParserStatus Status;
Status.setHasCodeCompletionAndIsError();
return Status;
}
/// Create a parser result with specified bits.
template <typename T>
static inline ParserResult<T> makeParserResult(ParserStatus Status,
T *Result) {
ParserResult<T> PR = Status.isError()
? makeParserErrorResult(Result)
: makeParserResult(Result);
if (Status.hasCodeCompletion())
PR.setHasCodeCompletion();
return PR;
}
template <typename T> ParserResult<T>::ParserResult(ParserStatus Status) {
assert(Status.isError());
setIsParseError();
if (Status.hasCodeCompletion())
setHasCodeCompletion();
}
} // namespace swift
#endif // LLVM_SWIFT_PARSER_PARSER_RESULT_H