Ejemplo n.º 1
0
/// \brief Takes a list of diagnostics that were expected to have been generated
/// but were not and produces a diagnostic to the user from this.
static unsigned PrintExpected(DiagnosticsEngine &Diags, const llvm::SourceMgr &SourceMgr,
                              DirectiveList &DL, DiagnosticsEngine::Level Kind) {
  if (DL.empty())
    return 0;

  for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
    Directive &D = **I;

    Diags.ReportError(D.DirectiveLoc,llvm::Twine("Inconsistend verify directive: ")+D.Text);
  }

  return DL.size();
}
Ejemplo n.º 2
0
 void
 DiagnoseFailures(DirectiveList & DL, DirectiveMap & DM) {
   for(typename DirectiveList::const_iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
     Underlying & Dir = **I;
     unsigned int Line = getCompilerInstance().getSourceManager().getExpansionLineNumber(Dir.getLocation());
     std::pair<typename DirectiveMap::iterator, typename DirectiveMap::iterator> rng = DM.equal_range(Line);
     switch (std::distance(rng.first, rng.second))
     {
       case 0:
         Diags.Report(Dir.getLocation(), ErrVerifyDiagNoCorrespondingDecl);
         break;
       default:
         // Delay evaluation of diagnostics until we are certain that
         // none of the statements on this line match
         llvm::SmallVector<MaybeDiagnostic, 4> diags;
         bool isGood = false;
         // TODO: If something is unexpected, then it should not appear in any of the subexpressions.
         for(typename DirectiveMap::iterator it = rng.first, end = rng.second; it != end; ++it) {
           MaybeDiagnostic MD = Dir.Match(it->second.second,Diags);
           // unexpected-<foo> must never fail
           if(Dir.isNegate()) {
             if(MD.isDiagnostic()) {
               diags.push_back(MD);
               isGood = false;
               break;
             }
           } else {
             if(MD.isDiagnostic())
               diags.push_back(MD);
             else
               isGood = true;
           }
         }
         if(!isGood) {
           for(std::size_t ii = 0; ii < diags.size(); ++ii)
             diags[ii].maybeReport();
         }
         break;
     }
   }
 }
Ejemplo n.º 3
0
/// CheckLists - Compare expected to seen diagnostic lists and return the
/// the difference between them.
///
static unsigned CheckLists(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
                           const char *Label,
                           DirectiveList &Left,
                           const_diag_iterator d2_begin,
                           const_diag_iterator d2_end) {
  DirectiveList LeftOnly;
  DiagList Right(d2_begin, d2_end);

  for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) {
    Directive& D = **I;
    unsigned LineNo1 = SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);

    for (unsigned i = 0; i < D.Max; ++i) {
      DiagList::iterator II, IE;
      for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
        unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first);
        if (LineNo1 != LineNo2)
          continue;

        if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first))
          continue;

        const std::string &RightText = II->second;
        if (D.match(RightText))
          break;
      }
      if (II == IE) {
        // Not found.
        if (i >= D.Min) break;
        LeftOnly.push_back(*I);
      } else {
        // Found. The same cannot be found twice.
        Right.erase(II);
      }
    }
  }
  // Now all that's left in Right are those that were not matched.
  unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label);
  num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label);
  return num;
}
Ejemplo n.º 4
0
static unsigned PrintProblem(Diagnostic &Diags, SourceManager *SourceMgr,
                             DirectiveList &DL, const char *Kind,
                             bool Expected) {
  if (DL.empty())
    return 0;

  llvm::SmallString<256> Fmt;
  llvm::raw_svector_ostream OS(Fmt);
  for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
    Directive& D = **I;
    if (D.Location.isInvalid() || !SourceMgr)
      OS << "\n  (frontend)";
    else
      OS << "\n  Line " << SourceMgr->getInstantiationLineNumber(D.Location);
    OS << ": " << D.Text;
  }

  Diags.Report(diag::err_verify_inconsistent_diags)
    << Kind << !Expected << OS.str();
  return DL.size();
}
Ejemplo n.º 5
0
/// \brief Takes a list of diagnostics that were expected to have been generated
/// but were not and produces a diagnostic to the user from this.
static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
                              DirectiveList &DL, const char *Kind) {
  if (DL.empty())
    return 0;

  SmallString<256> Fmt;
  llvm::raw_svector_ostream OS(Fmt);
  for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
    Directive &D = **I;
    OS << "\n  Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc);
    if (D.DirectiveLoc != D.DiagnosticLoc)
      OS << " (directive at "
         << SourceMgr.getFilename(D.DirectiveLoc) << ":"
         << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ")";
    OS << ": " << D.Text;
  }

  Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit()
    << Kind << /*Unexpected=*/false << OS.str();
  return DL.size();
}
Ejemplo n.º 6
0
/// CheckLists - Compare expected to seen diagnostic lists and return the
/// the difference between them.
///
static unsigned CheckLists(Diagnostic &Diags, SourceManager &SourceMgr,
                           const char *Label,
                           DirectiveList &Left,
                           const_diag_iterator d2_begin,
                           const_diag_iterator d2_end) {
  DirectiveList LeftOnly;
  DiagList Right(d2_begin, d2_end);

  for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) {
    Directive& D = **I;
    unsigned LineNo1 = SourceMgr.getInstantiationLineNumber(D.Location);

    for (unsigned i = 0; i < D.Count; ++i) {
      DiagList::iterator II, IE;
      for (II = Right.begin(), IE = Right.end(); II != IE; ++II) {
        unsigned LineNo2 = SourceMgr.getInstantiationLineNumber(II->first);
        if (LineNo1 != LineNo2)
          continue;

        const std::string &RightText = II->second;
        if (D.Match(RightText))
          break;
      }
      if (II == IE) {
        // Not found.
        LeftOnly.push_back(*I);
      } else {
        // Found. The same cannot be found twice.
        Right.erase(II);
      }
    }
  }
  // Now all that's left in Right are those that were not matched.

  return (PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true) +
          PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(),
                       Label, false));
}
Ejemplo n.º 7
0
/// \brief Takes a list of diagnostics that were expected to have been generated
/// but were not and produces a diagnostic to the user from this.
static unsigned PrintExpected(DiagnosticsEngine &Diags, llvm::SourceMgr &SourceMgr,
                              DirectiveList &DL, const char *Kind) {
  if (DL.empty())
    return 0;

  SmallString<256> Fmt;
  llvm::raw_svector_ostream OS(Fmt);
  for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) {
    Directive &D = **I;
    OS << "\n  Line " << SourceMgr.getLineAndColumn(D.DiagnosticLoc).first;
    if (D.DirectiveLoc != D.DiagnosticLoc) {
      int BufID = SourceMgr.FindBufferContainingLoc(D.DirectiveLoc);
      OS << " (directive at "
         << SourceMgr.getMemoryBuffer(BufID)->getBufferIdentifier()
         << SourceMgr.getLineAndColumn(D.DirectiveLoc).first << ")";
    }
    OS << ": " << D.Text;
  }

  Diags.Report(SourceLocation(), diag::verify_inconsistent_diags)
    << Kind << /*Unexpected=*/false << OS.str();
  return DL.size();
}
Ejemplo n.º 8
0
/// ParseDirective - Go through the comment and see if it indicates expected
/// diagnostics. If so, then put them in the appropriate directive list.
///
static void ParseDirective(const char *CommentStart, unsigned CommentLen,
                           ExpectedData &ED, Preprocessor &PP,
                           SourceLocation Pos) {
  // A single comment may contain multiple directives.
  for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) {
    // search for token: expected
    if (!PH.Search("expected"))
      break;
    PH.Advance();

    // next token: -
    if (!PH.Next("-"))
      continue;
    PH.Advance();

    // next token: { error | warning | note }
    DirectiveList* DL = NULL;
    if (PH.Next("error"))
      DL = &ED.Errors;
    else if (PH.Next("warning"))
      DL = &ED.Warnings;
    else if (PH.Next("note"))
      DL = &ED.Notes;
    else
      continue;
    PH.Advance();

    // default directive kind
    bool RegexKind = false;
    const char* KindStr = "string";

    // next optional token: -
    if (PH.Next("-re")) {
      PH.Advance();
      RegexKind = true;
      KindStr = "regex";
    }

    // skip optional whitespace
    PH.SkipWhitespace();

    // next optional token: positive integer
    unsigned Count = 1;
    if (PH.Next(Count))
      PH.Advance();

    // skip optional whitespace
    PH.SkipWhitespace();

    // next token: {{
    if (!PH.Next("{{")) {
      PP.Diag(Pos.getFileLocWithOffset(PH.C-PH.Begin),
              diag::err_verify_missing_start) << KindStr;
      continue;
    }
    PH.Advance();
    const char* const ContentBegin = PH.C; // mark content begin

    // search for token: }}
    if (!PH.Search("}}")) {
      PP.Diag(Pos.getFileLocWithOffset(PH.C-PH.Begin),
              diag::err_verify_missing_end) << KindStr;
      continue;
    }
    const char* const ContentEnd = PH.P; // mark content end
    PH.Advance();

    // build directive text; convert \n to newlines
    std::string Text;
    llvm::StringRef NewlineStr = "\\n";
    llvm::StringRef Content(ContentBegin, ContentEnd-ContentBegin);
    size_t CPos = 0;
    size_t FPos;
    while ((FPos = Content.find(NewlineStr, CPos)) != llvm::StringRef::npos) {
      Text += Content.substr(CPos, FPos-CPos);
      Text += '\n';
      CPos = FPos + NewlineStr.size();
    }
    if (Text.empty())
      Text.assign(ContentBegin, ContentEnd);

    // construct new directive
    Directive *D = Directive::Create(RegexKind, Pos, Text, Count);
    std::string Error;
    if (D->isValid(Error))
      DL->push_back(D);
    else {
      PP.Diag(Pos.getFileLocWithOffset(ContentBegin-PH.Begin),
              diag::err_verify_invalid_content)
        << KindStr << Error;
    }
  }
}
Ejemplo n.º 9
0
/// Go through the comment and see if it indicates expected
/// diagnostics. If so, then put them in the appropriate directive list.
///
/// Returns true if any valid directives were found.
static bool ParseDirective(StringRef S, ExpectedData *ED,
                           const llvm::SourceMgr &SM,
                           SourceLocation Pos, DiagnosticsEngine &Diags,
                           VerifyDiagnosticConsumer::DirectiveStatus &Status) {
  // A single comment may contain multiple directives.
  bool FoundDirective = false;
  for (ParseHelper PH(S); !PH.Done();) {
    // Search for token: expected
    if (!PH.Search("expected", true))
      break;
    PH.Advance();

    // Next token: -
    if (!PH.Next("-"))
      continue;
    PH.Advance();

    // Next token: { error | warning | note }
    DirectiveList* DL = NULL;
    if (PH.Next("diag"))
      DL = ED ? &ED->Errors : NULL;
    else if (PH.Next("note"))
      DL = ED ? &ED->Notes : NULL;
    else if (PH.Next("no-diagnostics")) {
      if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
        Diags.Report(Pos, diag::verify_invalid_no_diags)
          << /*IsExpectedNoDiagnostics=*/true;
      else
        Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
      continue;
    } else
      continue;
    PH.Advance();

    if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
      Diags.Report(Pos, diag::verify_invalid_no_diags)
          << /*IsExpectedNoDiagnostics=*/false;
      continue;
    }
    Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;

    // If a directive has been found but we're not interested
    // in storing the directive information, return now.
    if (!DL)
      return true;

    // Default directive kind.
    bool RegexKind = false;
    const char* KindStr = "string";

    // Next optional token: -
    if (PH.Next("-re")) {
      PH.Advance();
      RegexKind = true;
      KindStr = "regex";
    }

    // Next optional token: @
    SourceLocation ExpectedLoc;
    if (!PH.Next("@")) {
      ExpectedLoc = Pos;
    } else {
      PH.Advance();
      unsigned Line = 0;
      bool FoundPlus = PH.Next("+");
      if (FoundPlus || PH.Next("-")) {
        // Relative to current line.
        PH.Advance();
        unsigned ExpectedLine = SM.getLineAndColumn(Pos).first;
        if (PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
          if (FoundPlus) ExpectedLine += Line;
          else ExpectedLine -= Line;
          ExpectedLoc = locForLine(SM, Pos, ExpectedLine);
        }
      } else {
        // Absolute line number.
        if (PH.Next(Line) && Line > 0)
          ExpectedLoc = locForLine(SM, Pos, Line);
      }

      if (!ExpectedLoc.isValid()) {
        Diags.Report(Pos, diag::verify_missing_line) << KindStr;
        continue;
      }
      PH.Advance();
    }

    // Skip optional whitespace.
    PH.SkipWhitespace();

    // Next optional token: positive integer or a '+'.
    unsigned Min = 1;
    unsigned Max = 1;
    if (PH.Next(Min)) {
      PH.Advance();
      // A positive integer can be followed by a '+' meaning min
      // or more, or by a '-' meaning a range from min to max.
      if (PH.Next("+")) {
        Max = Directive::MaxCount;
        PH.Advance();
      } else if (PH.Next("-")) {
        PH.Advance();
        if (!PH.Next(Max) || Max < Min) {
          Diags.Report(Pos, diag::verify_invalid_range) << KindStr;
          continue;
        }
        PH.Advance();
      } else {
        Max = Min;
      }
    } else if (PH.Next("+")) {
      // '+' on its own means "1 or more".
      Max = Directive::MaxCount;
      PH.Advance();
    }

    // Skip optional whitespace.
    PH.SkipWhitespace();

    // Next token: {{
    if (!PH.Next("{{")) {
      Diags.Report(Pos, diag::verify_missing_start) << KindStr;
      continue;
    }
    PH.Advance();
    const char* const ContentBegin = PH.C; // mark content begin

    // Search for token: }}
    if (!PH.Search("}}")) {
      Diags.Report(Pos, diag::verify_missing_end) << KindStr;
      continue;
    }
    const char* const ContentEnd = PH.P; // mark content end
    PH.Advance();

    // Build directive text; convert \n to newlines.
    std::string Text;
    StringRef NewlineStr = "\\n";
    StringRef Content(ContentBegin, ContentEnd-ContentBegin);
    size_t CPos = 0;
    size_t FPos;
    while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
      Text += Content.substr(CPos, FPos-CPos);
      Text += '\n';
      CPos = FPos + NewlineStr.size();
    }
    if (Text.empty())
      Text.assign(ContentBegin, ContentEnd);

    // Construct new directive.
    Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text,
                                     Min, Max);
    std::string Error;
    if (D->isValid(Error)) {
      DL->push_back(D);
      FoundDirective = true;
    } else {
      Diags.Report(Pos, diag::verify_invalid_content) << KindStr << Error;
    }
  }

  return FoundDirective;
}
Ejemplo n.º 10
0
/// ParseDirective - Go through the comment and see if it indicates expected
/// diagnostics. If so, then put them in the appropriate directive list.
///
/// Returns true if any valid directives were found.
static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
                           Preprocessor *PP, SourceLocation Pos,
                           VerifyDiagnosticConsumer::DirectiveStatus &Status) {
  DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics();

  // A single comment may contain multiple directives.
  bool FoundDirective = false;
  for (ParseHelper PH(S); !PH.Done();) {
    // Search for token: expected
    if (!PH.Search("expected", true))
      break;
    PH.Advance();

    // Next token: -
    if (!PH.Next("-"))
      continue;
    PH.Advance();

    // Next token: { error | warning | note }
    DirectiveList *DL = nullptr;
    if (PH.Next("error"))
      DL = ED ? &ED->Errors : nullptr;
    else if (PH.Next("warning"))
      DL = ED ? &ED->Warnings : nullptr;
    else if (PH.Next("remark"))
      DL = ED ? &ED->Remarks : nullptr;
    else if (PH.Next("note"))
      DL = ED ? &ED->Notes : nullptr;
    else if (PH.Next("no-diagnostics")) {
      if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
        Diags.Report(Pos, diag::err_verify_invalid_no_diags)
          << /*IsExpectedNoDiagnostics=*/true;
      else
        Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
      continue;
    } else
      continue;
    PH.Advance();

    if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
      Diags.Report(Pos, diag::err_verify_invalid_no_diags)
        << /*IsExpectedNoDiagnostics=*/false;
      continue;
    }
    Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;

    // If a directive has been found but we're not interested
    // in storing the directive information, return now.
    if (!DL)
      return true;

    // Default directive kind.
    bool RegexKind = false;
    const char* KindStr = "string";

    // Next optional token: -
    if (PH.Next("-re")) {
      PH.Advance();
      RegexKind = true;
      KindStr = "regex";
    }

    // Next optional token: @
    SourceLocation ExpectedLoc;
    bool MatchAnyLine = false;
    if (!PH.Next("@")) {
      ExpectedLoc = Pos;
    } else {
      PH.Advance();
      unsigned Line = 0;
      bool FoundPlus = PH.Next("+");
      if (FoundPlus || PH.Next("-")) {
        // Relative to current line.
        PH.Advance();
        bool Invalid = false;
        unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
        if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
          if (FoundPlus) ExpectedLine += Line;
          else ExpectedLine -= Line;
          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
        }
      } else if (PH.Next(Line)) {
        // Absolute line number.
        if (Line > 0)
          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
      } else if (PP && PH.Search(":")) {
        // Specific source file.
        StringRef Filename(PH.C, PH.P-PH.C);
        PH.Advance();

        // Lookup file via Preprocessor, like a #include.
        const DirectoryLookup *CurDir;
        const FileEntry *FE =
            PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir,
                           nullptr, nullptr, nullptr);
        if (!FE) {
          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
                       diag::err_verify_missing_file) << Filename << KindStr;
          continue;
        }

        if (SM.translateFile(FE).isInvalid())
          SM.createFileID(FE, Pos, SrcMgr::C_User);

        if (PH.Next(Line) && Line > 0)
          ExpectedLoc = SM.translateFileLineCol(FE, Line, 1);
        else if (PH.Next("*")) {
          MatchAnyLine = true;
          ExpectedLoc = SM.translateFileLineCol(FE, 1, 1);
        }
      }

      if (ExpectedLoc.isInvalid()) {
        Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
                     diag::err_verify_missing_line) << KindStr;
        continue;
      }
      PH.Advance();
    }

    // Skip optional whitespace.
    PH.SkipWhitespace();

    // Next optional token: positive integer or a '+'.
    unsigned Min = 1;
    unsigned Max = 1;
    if (PH.Next(Min)) {
      PH.Advance();
      // A positive integer can be followed by a '+' meaning min
      // or more, or by a '-' meaning a range from min to max.
      if (PH.Next("+")) {
        Max = Directive::MaxCount;
        PH.Advance();
      } else if (PH.Next("-")) {
        PH.Advance();
        if (!PH.Next(Max) || Max < Min) {
          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
                       diag::err_verify_invalid_range) << KindStr;
          continue;
        }
        PH.Advance();
      } else {
        Max = Min;
      }
    } else if (PH.Next("+")) {
      // '+' on its own means "1 or more".
      Max = Directive::MaxCount;
      PH.Advance();
    }

    // Skip optional whitespace.
    PH.SkipWhitespace();

    // Next token: {{
    if (!PH.Next("{{")) {
      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
                   diag::err_verify_missing_start) << KindStr;
      continue;
    }
    PH.Advance();
    const char* const ContentBegin = PH.C; // mark content begin

    // Search for token: }}
    if (!PH.SearchClosingBrace("{{", "}}")) {
      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
                   diag::err_verify_missing_end) << KindStr;
      continue;
    }
    const char* const ContentEnd = PH.P; // mark content end
    PH.Advance();

    // Build directive text; convert \n to newlines.
    std::string Text;
    StringRef NewlineStr = "\\n";
    StringRef Content(ContentBegin, ContentEnd-ContentBegin);
    size_t CPos = 0;
    size_t FPos;
    while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
      Text += Content.substr(CPos, FPos-CPos);
      Text += '\n';
      CPos = FPos + NewlineStr.size();
    }
    if (Text.empty())
      Text.assign(ContentBegin, ContentEnd);

    // Check that regex directives contain at least one regex.
    if (RegexKind && Text.find("{{") == StringRef::npos) {
      Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
                   diag::err_verify_missing_regex) << Text;
      return false;
    }

    // Construct new directive.
    std::unique_ptr<Directive> D = Directive::create(
        RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max);

    std::string Error;
    if (D->isValid(Error)) {
      DL->push_back(std::move(D));
      FoundDirective = true;
    } else {
      Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
                   diag::err_verify_invalid_content)
        << KindStr << Error;
    }
  }

  return FoundDirective;
}
Ejemplo n.º 11
0
/// ParseDirective - Go through the comment and see if it indicates expected
/// diagnostics. If so, then put them in the appropriate directive list.
///
/// Returns true if any valid directives were found.
static bool ParseDirective(StringRef S, ExpectedData &ED, SourceManager &SM,
                           SourceLocation Pos, DiagnosticsEngine &Diags) {
  // A single comment may contain multiple directives.
  bool FoundDirective = false;
  for (ParseHelper PH(S); !PH.Done();) {
    // Search for token: expected
    if (!PH.Search("expected"))
      break;
    PH.Advance();

    // Next token: -
    if (!PH.Next("-"))
      continue;
    PH.Advance();

    // Next token: { error | warning | note }
    DirectiveList* DL = NULL;
    if (PH.Next("error"))
      DL = &ED.Errors;
    else if (PH.Next("warning"))
      DL = &ED.Warnings;
    else if (PH.Next("note"))
      DL = &ED.Notes;
    else
      continue;
    PH.Advance();

    // Default directive kind.
    bool RegexKind = false;
    const char* KindStr = "string";

    // Next optional token: -
    if (PH.Next("-re")) {
      PH.Advance();
      RegexKind = true;
      KindStr = "regex";
    }

    // Next optional token: @
    SourceLocation ExpectedLoc;
    if (!PH.Next("@")) {
      ExpectedLoc = Pos;
    } else {
      PH.Advance();
      unsigned Line = 0;
      bool FoundPlus = PH.Next("+");
      if (FoundPlus || PH.Next("-")) {
        // Relative to current line.
        PH.Advance();
        bool Invalid = false;
        unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid);
        if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) {
          if (FoundPlus) ExpectedLine += Line;
          else ExpectedLine -= Line;
          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1);
        }
      } else {
        // Absolute line number.
        if (PH.Next(Line) && Line > 0)
          ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1);
      }

      if (ExpectedLoc.isInvalid()) {
        Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
                     diag::err_verify_missing_line) << KindStr;
        continue;
      }
      PH.Advance();
    }

    // Skip optional whitespace.
    PH.SkipWhitespace();

    // Next optional token: positive integer or a '+'.
    unsigned Min = 1;
    unsigned Max = 1;
    if (PH.Next(Min)) {
      PH.Advance();
      // A positive integer can be followed by a '+' meaning min
      // or more, or by a '-' meaning a range from min to max.
      if (PH.Next("+")) {
        Max = Directive::MaxCount;
        PH.Advance();
      } else if (PH.Next("-")) {
        PH.Advance();
        if (!PH.Next(Max) || Max < Min) {
          Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
                       diag::err_verify_invalid_range) << KindStr;
          continue;
        }
        PH.Advance();
      } else {
        Max = Min;
      }
    } else if (PH.Next("+")) {
      // '+' on its own means "1 or more".
      Max = Directive::MaxCount;
      PH.Advance();
    }

    // Skip optional whitespace.
    PH.SkipWhitespace();

    // Next token: {{
    if (!PH.Next("{{")) {
      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
                   diag::err_verify_missing_start) << KindStr;
      continue;
    }
    PH.Advance();
    const char* const ContentBegin = PH.C; // mark content begin

    // Search for token: }}
    if (!PH.Search("}}")) {
      Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin),
                   diag::err_verify_missing_end) << KindStr;
      continue;
    }
    const char* const ContentEnd = PH.P; // mark content end
    PH.Advance();

    // Build directive text; convert \n to newlines.
    std::string Text;
    StringRef NewlineStr = "\\n";
    StringRef Content(ContentBegin, ContentEnd-ContentBegin);
    size_t CPos = 0;
    size_t FPos;
    while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) {
      Text += Content.substr(CPos, FPos-CPos);
      Text += '\n';
      CPos = FPos + NewlineStr.size();
    }
    if (Text.empty())
      Text.assign(ContentBegin, ContentEnd);

    // Construct new directive.
    Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text,
                                     Min, Max);
    std::string Error;
    if (D->isValid(Error)) {
      DL->push_back(D);
      FoundDirective = true;
    } else {
      Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin),
                   diag::err_verify_invalid_content)
        << KindStr << Error;
    }
  }

  return FoundDirective;
}