/// ConvertBackendLocation - Convert a location in a temporary llvm::SourceMgr /// buffer to be a valid FullSourceLoc. static FullSourceLoc ConvertBackendLocation(const llvm::SMDiagnostic &D, SourceManager &CSM) { // Get both the clang and llvm source managers. The location is relative to // a memory buffer that the LLVM Source Manager is handling, we need to add // a copy to the Clang source manager. const llvm::SourceMgr &LSM = *D.getSourceMgr(); // We need to copy the underlying LLVM memory buffer because llvm::SourceMgr // already owns its one and clang::SourceManager wants to own its one. const MemoryBuffer *LBuf = LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); // Create the copy and transfer ownership to clang::SourceManager. // TODO: Avoid copying files into memory. std::unique_ptr<llvm::MemoryBuffer> CBuf = llvm::MemoryBuffer::getMemBufferCopy(LBuf->getBuffer(), LBuf->getBufferIdentifier()); // FIXME: Keep a file ID map instead of creating new IDs for each location. FileID FID = CSM.createFileID(std::move(CBuf)); // Translate the offset into the file. unsigned Offset = D.getLoc().getPointer() - LBuf->getBufferStart(); SourceLocation NewLoc = CSM.getLocForStartOfFile(FID).getLocWithOffset(Offset); return FullSourceLoc(NewLoc, CSM); }
static FileID createInMemoryFile(StringRef FileName, MemoryBuffer *Source, SourceManager &Sources, FileManager &Files, vfs::InMemoryFileSystem *MemFS) { MemFS->addFileNoOwn(FileName, 0, Source); return Sources.createFileID(Files.getFile(FileName), SourceLocation(), SrcMgr::C_User); }
static bool ReadSourceLocation(FileManager &FM, SourceManager &SM, const char *&Memory, const char *MemoryEnd, SourceLocation &Location) { // Read the filename. unsigned FileNameLen = 0; if (ReadUnsigned(Memory, MemoryEnd, FileNameLen) || Memory + FileNameLen > MemoryEnd) return true; llvm::StringRef FileName(Memory, FileNameLen); Memory += FileNameLen; // Read the line, column. unsigned Line = 0, Column = 0; if (ReadUnsigned(Memory, MemoryEnd, Line) || ReadUnsigned(Memory, MemoryEnd, Column)) return true; if (FileName.empty()) { Location = SourceLocation(); return false; } const FileEntry *File = FM.getFile(FileName); if (!File) return true; // Make sure that this file has an entry in the source manager. if (!SM.hasFileInfo(File)) SM.createFileID(File, SourceLocation(), SrcMgr::C_User); Location = SM.getLocation(File, Line, Column); return false; }
static FileID createInMemoryFile(StringRef FileName, const MemoryBuffer *Source, SourceManager &Sources, FileManager &Files) { const FileEntry *Entry = Files.getVirtualFile(FileName == "-" ? "<stdin>" : FileName, Source->getBufferSize(), 0); Sources.overrideFileContents(Entry, Source, true); return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User); }
void clang::LoadSerializedDiagnostics(const llvm::sys::Path &DiagnosticsPath, unsigned num_unsaved_files, struct CXUnsavedFile *unsaved_files, FileManager &FileMgr, SourceManager &SourceMgr, SmallVectorImpl<StoredDiagnostic> &Diags) { using llvm::MemoryBuffer; using llvm::StringRef; MemoryBuffer *F = MemoryBuffer::getFile(DiagnosticsPath.c_str()); if (!F) return; // Enter the unsaved files into the file manager. for (unsigned I = 0; I != num_unsaved_files; ++I) { const FileEntry *File = FileMgr.getVirtualFile(unsaved_files[I].Filename, unsaved_files[I].Length, 0); if (!File) { // FIXME: Hard to localize when we have no diagnostics engine! Diags.push_back(StoredDiagnostic(Diagnostic::Fatal, (Twine("could not remap from missing file ") + unsaved_files[I].Filename).str())); delete F; return; } MemoryBuffer *Buffer = MemoryBuffer::getMemBuffer(unsaved_files[I].Contents, unsaved_files[I].Contents + unsaved_files[I].Length); if (!Buffer) { delete F; return; } SourceMgr.overrideFileContents(File, Buffer); SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User); } // Parse the diagnostics, emitting them one by one until we've // exhausted the data. StringRef Buffer = F->getBuffer(); const char *Memory = Buffer.data(), *MemoryEnd = Memory + Buffer.size(); while (Memory != MemoryEnd) { StoredDiagnostic Stored = StoredDiagnostic::Deserialize(FileMgr, SourceMgr, Memory, MemoryEnd); if (!Stored) break; Diags.push_back(Stored); } delete F; }
/// 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, VerifyDiagnosticConsumer::MarkerTracker &Markers) { DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); // First, scan the comment looking for markers. for (ParseHelper PH(S); !PH.Done();) { if (!PH.Search("#", true)) break; PH.C = PH.P; if (!PH.NextMarker()) { PH.Next("#"); PH.Advance(); continue; } PH.Advance(); Markers.addMarker(PH.Match(), Pos); } // A single comment may contain multiple directives. bool FoundDirective = false; for (ParseHelper PH(S); !PH.Done();) { // Search for the initial directive token. // If one prefix, save time by searching only for its directives. // Otherwise, search for any potential directive token and check it later. const auto &Prefixes = Diags.getDiagnosticOptions().VerifyPrefixes; if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(), true, true) : PH.Search("", true, true))) break; StringRef DToken = PH.Match(); PH.Advance(); // Default directive kind. UnattachedDirective D; const char *KindStr = "string"; // Parse the initial directive token in reverse so we can easily determine // its exact actual prefix. If we were to parse it from the front instead, // it would be harder to determine where the prefix ends because there // might be multiple matching -verify prefixes because some might prefix // others. // Regex in initial directive token: -re if (DToken.endswith("-re")) { D.RegexKind = true; KindStr = "regex"; DToken = DToken.substr(0, DToken.size()-3); } // Type in initial directive token: -{error|warning|note|no-diagnostics} bool NoDiag = false; StringRef DType; if (DToken.endswith(DType="-error")) D.DL = ED ? &ED->Errors : nullptr; else if (DToken.endswith(DType="-warning")) D.DL = ED ? &ED->Warnings : nullptr; else if (DToken.endswith(DType="-remark")) D.DL = ED ? &ED->Remarks : nullptr; else if (DToken.endswith(DType="-note")) D.DL = ED ? &ED->Notes : nullptr; else if (DToken.endswith(DType="-no-diagnostics")) { NoDiag = true; if (D.RegexKind) continue; } else continue; DToken = DToken.substr(0, DToken.size()-DType.size()); // What's left in DToken is the actual prefix. That might not be a -verify // prefix even if there is only one -verify prefix (for example, the full // DToken is foo-bar-warning, but foo is the only -verify prefix). if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken)) continue; if (NoDiag) { if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) Diags.Report(Pos, diag::err_verify_invalid_no_diags) << /*IsExpectedNoDiagnostics=*/true; else Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; continue; } 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 (!D.DL) return true; // Next optional token: @ SourceLocation ExpectedLoc; StringRef Marker; 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 (PH.NextMarker()) { Marker = PH.Match(); } 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, 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); } } else if (PH.Next("*")) { MatchAnyLine = true; ExpectedLoc = SourceLocation(); } if (ExpectedLoc.isInvalid() && !MatchAnyLine && Marker.empty()) { 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 '+'. if (PH.Next(D.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("+")) { D.Max = Directive::MaxCount; PH.Advance(); } else if (PH.Next("-")) { PH.Advance(); if (!PH.Next(D.Max) || D.Max < D.Min) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_invalid_range) << KindStr; continue; } PH.Advance(); } else { D.Max = D.Min; } } else if (PH.Next("+")) { // '+' on its own means "1 or more". D.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(); D.DirectivePos = Pos; D.ContentBegin = Pos.getLocWithOffset(ContentBegin - PH.Begin); // Build directive text; convert \n to newlines. StringRef NewlineStr = "\\n"; StringRef Content(ContentBegin, ContentEnd-ContentBegin); size_t CPos = 0; size_t FPos; while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { D.Text += Content.substr(CPos, FPos-CPos); D.Text += '\n'; CPos = FPos + NewlineStr.size(); } if (D.Text.empty()) D.Text.assign(ContentBegin, ContentEnd); // Check that regex directives contain at least one regex. if (D.RegexKind && D.Text.find("{{") == StringRef::npos) { Diags.Report(D.ContentBegin, diag::err_verify_missing_regex) << D.Text; return false; } if (Marker.empty()) attachDirective(Diags, D, ExpectedLoc, MatchAnyLine); else Markers.addDirective(Marker, D); FoundDirective = true; } return FoundDirective; }
/// 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; }