示例#1
0
URI::URI(llvm::StringRef Scheme, llvm::StringRef Authority,
         llvm::StringRef Body)
    : Scheme(Scheme), Authority(Authority), Body(Body) {
  assert(!Scheme.empty());
  assert((Authority.empty() || Body.startswith("/")) &&
         "URI body must start with '/' when authority is present.");
}
示例#2
0
static inline void StripSpaces(llvm::StringRef &Str)
{
    while (!Str.empty() && isspace(Str[0]))
        Str = Str.substr(1);
    while (!Str.empty() && isspace(Str.back()))
        Str = Str.substr(0, Str.size()-1);
}
示例#3
0
bool BreakpointID::StringIsBreakpointName(llvm::StringRef str, Status &error) {
  error.Clear();
  if (str.empty())
  {
    error.SetErrorStringWithFormat("Empty breakpoint names are not allowed");
    return false;
  }

  // First character must be a letter or _
  if (!isalpha(str[0]) && str[0] != '_')
  {
    error.SetErrorStringWithFormat("Breakpoint names must start with a "
                                   "character or underscore: %s",
                                   str.str().c_str());
    return false;
  }

  // Cannot contain ., -, or space.
  if (str.find_first_of(".- ") != llvm::StringRef::npos) {
    error.SetErrorStringWithFormat("Breakpoint names cannot contain "
                                   "'.' or '-': \"%s\"",
                                   str.str().c_str());
    return false;
  }

  return true;
}
示例#4
0
lldb::SymbolType
ObjectFile::GetSymbolTypeFromName(llvm::StringRef name,
                                  lldb::SymbolType symbol_type_hint) {
  if (!name.empty()) {
    if (name.startswith("_T")) {
      // Swift
      if (name.startswith("_TM"))
        return lldb::eSymbolTypeMetadata;
      if (name.startswith("_TWvd"))
        return lldb::eSymbolTypeIVarOffset;
    } else if (name.startswith("_OBJC_")) {
      // ObjC
      if (name.startswith("_OBJC_CLASS_$_"))
        return lldb::eSymbolTypeObjCClass;
      if (name.startswith("_OBJC_METACLASS_$_"))
        return lldb::eSymbolTypeObjCMetaClass;
      if (name.startswith("_OBJC_IVAR_$_"))
        return lldb::eSymbolTypeObjCIVar;
    } else if (name.startswith(".objc_class_name_")) {
      // ObjC v1
      return lldb::eSymbolTypeObjCClass;
    }
  }
  return symbol_type_hint;
}
示例#5
0
Status PipeWindows::CreateNew(llvm::StringRef name,
                              bool child_process_inherit) {
  if (name.empty())
    return Status(ERROR_INVALID_PARAMETER, eErrorTypeWin32);

  if (CanRead() || CanWrite())
    return Status(ERROR_ALREADY_EXISTS, eErrorTypeWin32);

  std::string pipe_path = "\\\\.\\Pipe\\";
  pipe_path.append(name);

  // Always open for overlapped i/o.  We implement blocking manually in Read and
  // Write.
  DWORD read_mode = FILE_FLAG_OVERLAPPED;
  m_read = ::CreateNamedPipeA(
      pipe_path.c_str(), PIPE_ACCESS_INBOUND | read_mode,
      PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL);
  if (INVALID_HANDLE_VALUE == m_read)
    return Status(::GetLastError(), eErrorTypeWin32);
  m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY);
  ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
  m_read_overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);

  // Open the write end of the pipe.
  Status result = OpenNamedPipe(name, child_process_inherit, false);
  if (!result.Success()) {
    CloseReadFileDescriptor();
    return result;
  }

  return result;
}
示例#6
0
void clang::AttachHeaderIncludeGen(Preprocessor &PP, bool ShowAllHeaders,
                                   llvm::StringRef OutputPath) {
  llvm::raw_ostream *OutputFile = &llvm::errs();
  bool OwnsOutputFile = false;

  // Open the output file, if used.
  if (!OutputPath.empty()) {
    std::string Error;
    llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream(
      OutputPath.str().c_str(), Error, llvm::raw_fd_ostream::F_Append);
    if (!Error.empty()) {
      PP.getDiagnostics().Report(
        clang::diag::warn_fe_cc_print_header_failure) << Error;
      delete OS;
    } else {
      OS->SetUnbuffered();
      OS->SetUseAtomicWrites(true);
      OutputFile = OS;
      OwnsOutputFile = true;
    }
  }

  PP.addPPCallbacks(new HeaderIncludesCallback(&PP, ShowAllHeaders,
                                               OutputFile, OwnsOutputFile));
}
示例#7
0
 void MetaProcessor::setFileStream(llvm::StringRef file, bool append, int fd,
             llvm::SmallVector<llvm::SmallString<128>, 2>& prevFileStack) {
   // If we have a fileName to redirect to store it.
   if (!file.empty()) {
     prevFileStack.push_back(file);
     // pop and push a null terminating 0.
     // SmallVectorImpl<T> does not have a c_str(), thus instead of casting to
     // a SmallString<T> we null terminate the data that we have and pop the
     // 0 char back.
     prevFileStack.back().push_back(0);
     prevFileStack.back().pop_back();
     if (!append) {
       FILE * f;
       if (!(f = fopen(file.data(), "w"))) {
         llvm::errs() << "cling::MetaProcessor::setFileStream:"
                      " The file path " << file.data() << "is not valid.";
       } else {
         fclose(f);
       }
     }
   // Else unredirection, so switch to the previous file.
   } else {
     // If there is no previous file on the stack we pop the file
     if (!prevFileStack.empty()) {
       prevFileStack.pop_back();
     }
   }
 }
示例#8
0
 void MetaSema::actOngCommand(llvm::StringRef varName) const {
   if (varName.empty())
     DisplayGlobals(m_MetaProcessor.getOuts(), &m_Interpreter);
   else
     DisplayGlobal(m_MetaProcessor.getOuts(),
                   &m_Interpreter, varName.str().c_str());
 }
示例#9
0
 void MetaSema::actOnTypedefCommand(llvm::StringRef typedefName) const {
   if (typedefName.empty())
     DisplayTypedefs(m_MetaProcessor.getOuts(), &m_Interpreter);
   else
     DisplayTypedef(m_MetaProcessor.getOuts(),
                    &m_Interpreter, typedefName.str().c_str());
 }
示例#10
0
size_t DiagnosticManager::PutString(DiagnosticSeverity severity,
                                    llvm::StringRef str) {
    if (str.empty())
        return 0;
    AddDiagnostic(str, severity, eDiagnosticOriginLLDB);
    return str.size();
}
示例#11
0
 void MetaSema::actOnclassCommand(llvm::StringRef className) const {
   if (!className.empty())
     DisplayClass(m_MetaProcessor.getOuts(),
                  &m_Interpreter, className.str().c_str(), true);
   else
     DisplayClasses(m_MetaProcessor.getOuts(), &m_Interpreter, false);
 }
示例#12
0
// "OutputEvent": {
//   "allOf": [ { "$ref": "#/definitions/Event" }, {
//     "type": "object",
//     "description": "Event message for 'output' event type. The event
//                     indicates that the target has produced some output.",
//     "properties": {
//       "event": {
//         "type": "string",
//         "enum": [ "output" ]
//       },
//       "body": {
//         "type": "object",
//         "properties": {
//           "category": {
//             "type": "string",
//             "description": "The output category. If not specified,
//                             'console' is assumed.",
//             "_enum": [ "console", "stdout", "stderr", "telemetry" ]
//           },
//           "output": {
//             "type": "string",
//             "description": "The output to report."
//           },
//           "variablesReference": {
//             "type": "number",
//             "description": "If an attribute 'variablesReference' exists
//                             and its value is > 0, the output contains
//                             objects which can be retrieved by passing
//                             variablesReference to the VariablesRequest."
//           },
//           "source": {
//             "$ref": "#/definitions/Source",
//             "description": "An optional source location where the output
//                             was produced."
//           },
//           "line": {
//             "type": "integer",
//             "description": "An optional source location line where the
//                             output was produced."
//           },
//           "column": {
//             "type": "integer",
//             "description": "An optional source location column where the
//                             output was produced."
//           },
//           "data": {
//             "type":["array","boolean","integer","null","number","object",
//                     "string"],
//             "description": "Optional data to report. For the 'telemetry'
//                             category the data will be sent to telemetry, for
//                             the other categories the data is shown in JSON
//                             format."
//           }
//         },
//         "required": ["output"]
//       }
//     },
//     "required": [ "event", "body" ]
//   }]
// }
void VSCode::SendOutput(OutputType o, const llvm::StringRef output) {
  if (output.empty())
    return;

  llvm::json::Object event(CreateEventObject("output"));
  llvm::json::Object body;
  const char *category = nullptr;
  switch (o) {
  case OutputType::Console:
    category = "console";
    break;
  case OutputType::Stdout:
    category = "stdout";
    break;
  case OutputType::Stderr:
    category = "stderr";
    break;
  case OutputType::Telemetry:
    category = "telemetry";
    break;
  }
  body.try_emplace("category", category);
  EmplaceSafeString(body, "output", output.str());
  event.try_emplace("body", std::move(body));
  SendJSON(llvm::json::Value(std::move(event)));
}
示例#13
0
static bool typeSuffix(llvm::StringRef suffix,
                       TypePtr defaultType,
                       llvm::StringRef testSuffix,
                       TypePtr testDefaultType)
{
    return suffix == testSuffix || (suffix.empty() && defaultType == testDefaultType);
}
示例#14
0
bool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI,
                                      llvm::StringRef Filename, InputKind Kind,
                                      DiagnosticClient *DiagClient,
                                      llvm::StringRef outputDir) {
  assert(!outputDir.empty() && "Expected output directory path");
  return applyTransforms(origCI, Filename, Kind, DiagClient, outputDir);
}
static std::string getDiagCategoryEnum(llvm::StringRef name) {
  if (name.empty())
    return "DiagCat_None";
  SmallString<256> enumName = llvm::StringRef("DiagCat_");
  for (llvm::StringRef::iterator I = name.begin(), E = name.end(); I != E; ++I)
    enumName += isalnum(*I) ? *I : '_';
  return enumName.str();
}
示例#16
0
void RefactoringFactories::registerRefactoring(
  llvm::StringRef name,
  RefactoringFactory factory)
{
  assert(!name.empty() && factory);

  factories_[name] = factory;
}
示例#17
0
lldb::OptionValueSP
OptionValueArray::GetSubValue(const ExecutionContext *exe_ctx,
                              llvm::StringRef name, bool will_modify,
                              Status &error) const {
  if (name.empty() || name.front() != '[') {
    error.SetErrorStringWithFormat(
      "invalid value path '%s', %s values only support '[<index>]' subvalues "
      "where <index> is a positive or negative array index",
      name.str().c_str(), GetTypeAsCString());
    return nullptr;
  }

  name = name.drop_front();
  llvm::StringRef index, sub_value;
  std::tie(index, sub_value) = name.split(']');
  if (index.size() == name.size()) {
    // Couldn't find a closing bracket
    return nullptr;
  }

  const size_t array_count = m_values.size();
  int32_t idx = 0;
  if (index.getAsInteger(0, idx))
    return nullptr;

  uint32_t new_idx = UINT32_MAX;
  if (idx < 0) {
    // Access from the end of the array if the index is negative
    new_idx = array_count - idx;
  } else {
    // Just a standard index
    new_idx = idx;
  }

  if (new_idx < array_count) {
    if (m_values[new_idx]) {
      if (!sub_value.empty())
        return m_values[new_idx]->GetSubValue(exe_ctx, sub_value,
                                              will_modify, error);
      else
        return m_values[new_idx];
    }
  } else {
    if (array_count == 0)
      error.SetErrorStringWithFormat(
          "index %i is not valid for an empty array", idx);
    else if (idx > 0)
      error.SetErrorStringWithFormat(
          "index %i out of range, valid values are 0 through %" PRIu64,
          idx, (uint64_t)(array_count - 1));
    else
      error.SetErrorStringWithFormat("negative index %i out of range, "
                                      "valid values are -1 through "
                                      "-%" PRIu64,
                                      idx, (uint64_t)array_count);
  }
  return OptionValueSP();
}
示例#18
0
void IncludeStructure::recordInclude(llvm::StringRef IncludingName,
                                     llvm::StringRef IncludedName,
                                     llvm::StringRef IncludedRealName) {
  auto Child = fileIndex(IncludedName);
  if (!IncludedRealName.empty() && RealPathNames[Child].empty())
    RealPathNames[Child] = IncludedRealName;
  auto Parent = fileIndex(IncludingName);
  IncludeChildren[Parent].push_back(Child);
}
void InitHeaderSearch::AddDelimitedPaths(llvm::StringRef at) {
  if (at.empty()) // Empty string should not add '.' path.
    return;

  llvm::StringRef::size_type delim;
  while ((delim = at.find(llvm::sys::PathSeparator)) != llvm::StringRef::npos) {
    if (delim == 0)
      AddPath(".", Angled, false, true, false);
    else
      AddPath(at.substr(0, delim), Angled, false, true, false);
    at = at.substr(delim + 1);
  }

  if (at.empty())
    AddPath(".", Angled, false, true, false);
  else
    AddPath(at, Angled, false, true, false);
}
示例#20
0
    void redirect(llvm::StringRef file, bool apnd,
                  MetaProcessor::RedirectionScope scope) {
      if (file.empty()) {
        // Unredirection, remove last redirection state(s) for given scope(s)
        if (m_Stack.empty()) {
          cling::errs() << "No redirections left to remove\n";
          return;
        }

        MetaProcessor::RedirectionScope lScope = scope;
        SmallVector<RedirectStack::iterator, 2> Remove;
        for (auto it = m_Stack.rbegin(), e = m_Stack.rend(); it != e; ++it) {
          Redirect *R = (*it).get();
          const unsigned Match = R->Scope & lScope;
          if (Match) {
#ifdef LLVM_ON_WIN32
            // stdout back from stderr, fix up our console output on destruction
            if (m_TTY && R->FD == m_Bak[1] && scope & kSTDOUT)
              m_TTY = 2;
#endif
            // Clear the flag so restore below will ignore R for scope
            R->Scope = MetaProcessor::RedirectionScope(R->Scope & ~Match);
            // If no scope left, then R should be removed
            if (!R->Scope) {
              // standard [24.4.1/1] says &*(reverse_iterator(i)) == &*(i - 1)
              Remove.push_back(std::next(it).base());
            }
            // Clear match to reduce lScope (kSTDBOTH -> kSTDOUT or kSTDERR)
            lScope = MetaProcessor::RedirectionScope(lScope & ~Match);
            // If nothing to match anymore, then we're done
            if (!lScope)
              break;
          }
        }
        // std::vector::erase invalidates iterators at or after the point of
        // the erase, so if we reverse iterate on Remove everything is fine
        for (auto it = Remove.rbegin(), e = Remove.rend(); it != e; ++it)
          m_Stack.erase(*it);
      } else {
        // Add new redirection state
        if (push(new Redirect(file.str(), apnd, scope, m_Bak)) != kInvalidFD) {
          // Save a backup for the scope(s), if not already done
          if (scope & MetaProcessor::kSTDOUT)
            dupOnce(STDOUT_FILENO, m_Bak[0]);
          if (scope & MetaProcessor::kSTDERR)
            dupOnce(STDERR_FILENO, m_Bak[1]);
        } else
          return; // Failure
      }

      if (scope & MetaProcessor::kSTDOUT)
        m_CurStdOut =
            restore(STDOUT_FILENO, stdout, MetaProcessor::kSTDOUT, m_Bak[0]);
      if (scope & MetaProcessor::kSTDERR)
        restore(STDERR_FILENO, stderr, MetaProcessor::kSTDERR, m_Bak[1]);
    }
示例#21
0
void ClangDocBitcodeWriter::emitRecord(llvm::StringRef Str, RecordId ID) {
  assert(RecordIdNameMap[ID] && "Unknown RecordId.");
  assert(RecordIdNameMap[ID].Abbrev == &StringAbbrev &&
         "Abbrev type mismatch.");
  if (!prepRecordData(ID, !Str.empty()))
    return;
  assert(Str.size() < (1U << BitCodeConstants::StringLengthSize));
  Record.push_back(Str.size());
  Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, Str);
}
示例#22
0
MigrationProcess::MigrationProcess(const CompilerInvocation &CI,
                                   DiagnosticClient *diagClient,
                                   llvm::StringRef outputDir)
  : OrigCI(CI), DiagClient(diagClient) {
  if (!outputDir.empty()) {
    llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
    llvm::IntrusiveRefCntPtr<Diagnostic> Diags(
                 new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false));
    Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true);
  }
}
示例#23
0
void Log::WriteHeader(llvm::raw_ostream &OS, llvm::StringRef file,
                      llvm::StringRef function) {
  Flags options = GetOptions();
  static uint32_t g_sequence_id = 0;
  // Add a sequence ID if requested
  if (options.Test(LLDB_LOG_OPTION_PREPEND_SEQUENCE))
    OS << ++g_sequence_id << " ";

  // Timestamp if requested
  if (options.Test(LLDB_LOG_OPTION_PREPEND_TIMESTAMP)) {
    auto now = std::chrono::duration<double>(
        std::chrono::system_clock::now().time_since_epoch());
    OS << llvm::formatv("{0:f9} ", now.count());
  }

  // Add the process and thread if requested
  if (options.Test(LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD))
    OS << llvm::formatv("[{0,0+4}/{1,0+4}] ", getpid(),
                        llvm::get_threadid());

  // Add the thread name if requested
  if (options.Test(LLDB_LOG_OPTION_PREPEND_THREAD_NAME)) {
    llvm::SmallString<32> thread_name;
    llvm::get_thread_name(thread_name);

    llvm::SmallString<12> format_str;
    llvm::raw_svector_ostream format_os(format_str);
    format_os << "{0,-" << llvm::alignTo<16>(thread_name.size()) << "} ";
    OS << llvm::formatv(format_str.c_str(), thread_name);
  }

  if (options.Test(LLDB_LOG_OPTION_BACKTRACE))
    llvm::sys::PrintStackTrace(OS);

  if (options.Test(LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION) &&
      (!file.empty() || !function.empty())) {
    file = llvm::sys::path::filename(file).take_front(40);
    function = function.take_front(40);
    OS << llvm::formatv("{0,-60:60} ", (file + ":" + function).str());
  }
}
示例#24
0
std::unique_ptr<Refactoring> RefactoringFactories::create(
  llvm::StringRef name,
  const RefactoringArgs& args)
{
  assert(!name.empty());

  auto iter = factories_.find(name);
  if (iter == factories_.end())
    return { };

  return iter->second(name, args);
}
示例#25
0
 void tag(llvm::StringRef className, clang::SourceRange range, llvm::StringRef ref = llvm::StringRef()) {
     int len = range.getEnd().getRawEncoding() - range.getBegin().getRawEncoding() + 1;
     if (len > 0) {
         std::string attr;
         if (ref.empty()) {
             attr = "class=\"" % className % "\"";
         } else {
             attr = "class=\"" % className % "\" data-ref=\"" % ref % "\"";
         }
         generator.addTag("span", attr, range.getBegin().getRawEncoding(), len);
     }
 }
示例#26
0
  int MetaProcessor::process(llvm::StringRef input_line,
                             Interpreter::CompilationResult& compRes,
                             Value* result,
                             bool disableValuePrinting /* = false */) {
    if (result)
      *result = Value();
    compRes = Interpreter::kSuccess;
    int expectedIndent = m_InputValidator->getExpectedIndent();

    if (expectedIndent)
      compRes = Interpreter::kMoreInputExpected;

    if (input_line.empty() ||
        (input_line.size() == 1 && input_line.front() == '\n')) {
      // just a blank line, nothing to do.
      return expectedIndent;
    }

    //  Check for and handle meta commands.
    m_MetaParser->enterNewInputLine(input_line);
    MetaSema::ActionResult actionResult = MetaSema::AR_Success;
    if (!m_InputValidator->inBlockComment() &&
         m_MetaParser->isMetaCommand(actionResult, result)) {

      if (m_MetaParser->isQuitRequested())
        return -1;

      if (actionResult != MetaSema::AR_Success)
        compRes = Interpreter::kFailure;
       // ExpectedIndent might have changed after meta command.
       return m_InputValidator->getExpectedIndent();
    }

    // Check if the current statement is now complete. If not, return to
    // prompt for more.
    if (m_InputValidator->validate(input_line) == InputValidator::kIncomplete) {
      compRes = Interpreter::kMoreInputExpected;
      return m_InputValidator->getExpectedIndent();
    }

    //  We have a complete statement, compile and execute it.
    std::string input;
    m_InputValidator->reset(&input);
    // if (m_Options.RawInput)
    //   compResLocal = m_Interp.declare(input);
    // else
    compRes = m_Interp.process(input, result, /*Transaction*/ nullptr,
                               disableValuePrinting);

    return 0;
  }
示例#27
0
bool Breakpoint::AddName(llvm::StringRef new_name, Status &error) {
  if (new_name.empty())
    return false;
  if (!BreakpointID::StringIsBreakpointName(new_name, error)) {
    error.SetErrorStringWithFormatv("input name \"{0}\" not a breakpoint name.",
                                    new_name);
    return false;
  }
  if (!error.Success())
    return false;

  m_name_list.insert(new_name);
  return true;
}
lldb::OptionValueSP
OptionValueDictionary::GetSubValue(const ExecutionContext *exe_ctx,
                                   llvm::StringRef name, bool will_modify,
                                   Status &error) const {
  lldb::OptionValueSP value_sp;
  if (name.empty())
    return nullptr;

  llvm::StringRef left, temp;
  std::tie(left, temp) = name.split('[');
  if (left.size() == name.size()) {
    error.SetErrorStringWithFormat("invalid value path '%s', %s values only "
      "support '[<key>]' subvalues where <key> "
      "a string value optionally delimited by "
      "single or double quotes",
      name.str().c_str(), GetTypeAsCString());
    return nullptr;
  }
  assert(!temp.empty());

  llvm::StringRef key, quote_char;

  if (temp[0] == '\"' || temp[0] == '\'') {
    quote_char = temp.take_front();
    temp = temp.drop_front();
  }

  llvm::StringRef sub_name;
  std::tie(key, sub_name) = temp.split(']');

  if (!key.consume_back(quote_char) || key.empty()) {
    error.SetErrorStringWithFormat("invalid value path '%s', "
      "key names must be formatted as ['<key>'] where <key> "
      "is a string that doesn't contain quotes and the quote"
      " char is optional", name.str().c_str());
    return nullptr;
  }

  value_sp = GetValueForKey(ConstString(key));
  if (!value_sp) {
    error.SetErrorStringWithFormat(
      "dictionary does not contain a value for the key name '%s'",
      key.str().c_str());
    return nullptr;
  }

  if (sub_name.empty())
    return value_sp;
  return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error);
}
示例#29
0
uint32_t
PythonFile::GetOptionsFromMode(llvm::StringRef mode)
{
    if (mode.empty())
        return 0;

    return llvm::StringSwitch<uint32_t>(mode.str().c_str())
           .Case("r",   File::eOpenOptionRead)
           .Case("w",   File::eOpenOptionWrite)
           .Case("a",   File::eOpenOptionWrite|File::eOpenOptionAppend|File::eOpenOptionCanCreate)
           .Case("r+",  File::eOpenOptionRead|File::eOpenOptionWrite)
           .Case("w+",  File::eOpenOptionRead|File::eOpenOptionWrite|File::eOpenOptionCanCreate|File::eOpenOptionTruncate)
           .Case("a+",  File::eOpenOptionRead|File::eOpenOptionWrite|File::eOpenOptionAppend|File::eOpenOptionCanCreate)
           .Default(0);
}
示例#30
0
llvm::Optional<BreakpointID>
BreakpointID::ParseCanonicalReference(llvm::StringRef input) {
  break_id_t bp_id;
  break_id_t loc_id = LLDB_INVALID_BREAK_ID;

  if (input.empty())
    return llvm::None;

  // If it doesn't start with an integer, it's not valid.
  if (input.consumeInteger(0, bp_id))
    return llvm::None;

  // period is optional, but if it exists, it must be followed by a number.
  if (input.consume_front(".")) {
    if (input.consumeInteger(0, loc_id))
      return llvm::None;
  }

  // And at the end, the entire string must have been consumed.
  if (!input.empty())
    return llvm::None;

  return BreakpointID(bp_id, loc_id);
}