static void escapeAndPrintString(llvm::raw_ostream &os, StringRef Str) { if (Str.empty()) { // Special-case the empty string. os << "\"\""; return; } bool NeedsEscape = Str.find_first_of(" \"\\$") != StringRef::npos; if (!NeedsEscape) { // This string doesn't have anything we need to escape, so print it directly os << Str; return; } // Quote and escape. This isn't really complete, but is good enough, and // matches how Clang's Command handles escaping arguments. os << '"'; for (const char c : Str) { switch (c) { case '"': case '\\': case '$': // These characters need to be escaped. os << '\\'; // Fall-through to the default case, since we still need to print the // character. SWIFT_FALLTHROUGH; default: os << c; } } os << '"'; }
std::string get_line_from_offset(StringRef buffer, std::size_t offset) { assert(buffer.size() > offset); auto line_start = buffer.find_last_of("\r\n", offset) + 1; auto line_end = buffer.find_first_of("\r\n", offset); return std::string(buffer.begin() + line_start, buffer.begin() + line_end); }
/// Print the filename, with escaping or quoting that accommodates the three /// most likely tools that use dependency files: GNU Make, BSD Make, and /// NMake/Jom. /// /// BSD Make is the simplest case: It does no escaping at all. This means /// characters that are normally delimiters, i.e. space and # (the comment /// character) simply aren't supported in filenames. /// /// GNU Make does allow space and # in filenames, but to avoid being treated /// as a delimiter or comment, these must be escaped with a backslash. Because /// backslash is itself the escape character, if a backslash appears in a /// filename, it should be escaped as well. (As a special case, $ is escaped /// as $$, which is the normal Make way to handle the $ character.) /// For compatibility with BSD Make and historical practice, if GNU Make /// un-escapes characters in a filename but doesn't find a match, it will /// retry with the unmodified original string. /// /// GCC tries to accommodate both Make formats by escaping any space or # /// characters in the original filename, but not escaping backslashes. The /// apparent intent is so that filenames with backslashes will be handled /// correctly by BSD Make, and by GNU Make in its fallback mode of using the /// unmodified original string; filenames with # or space characters aren't /// supported by BSD Make at all, but will be handled correctly by GNU Make /// due to the escaping. /// /// A corner case that GCC gets only partly right is when the original filename /// has a backslash immediately followed by space or #. GNU Make would expect /// this backslash to be escaped; however GCC escapes the original backslash /// only when followed by space, not #. It will therefore take a dependency /// from a directive such as /// #include "a\ b\#c.h" /// and emit it as /// a\\\ b\\#c.h /// which GNU Make will interpret as /// a\ b\ /// followed by a comment. Failing to find this file, it will fall back to the /// original string, which probably doesn't exist either; in any case it won't /// find /// a\ b\#c.h /// which is the actual filename specified by the include directive. /// /// Clang does what GCC does, rather than what GNU Make expects. /// /// NMake/Jom has a different set of scary characters, but wraps filespecs in /// double-quotes to avoid misinterpreting them; see /// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info, /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx /// for Windows file-naming info. static void PrintFilename(raw_ostream &OS, StringRef Filename, DependencyOutputFormat OutputFormat) { if (OutputFormat == DependencyOutputFormat::NMake) { // Add quotes if needed. These are the characters listed as "special" to // NMake, that are legal in a Windows filespec, and that could cause // misinterpretation of the dependency string. if (Filename.find_first_of(" #${}^!") != StringRef::npos) OS << '\"' << Filename << '\"'; else OS << Filename; return; } assert(OutputFormat == DependencyOutputFormat::Make); for (unsigned i = 0, e = Filename.size(); i != e; ++i) { if (Filename[i] == '#') // Handle '#' the broken gcc way. OS << '\\'; else if (Filename[i] == ' ') { // Handle space correctly. OS << '\\'; unsigned j = i; while (j > 0 && Filename[--j] == '\\') OS << '\\'; } else if (Filename[i] == '$') // $ is escaped by $$. OS << '$'; OS << Filename[i]; } }
static void outputReplacementXML(StringRef Text) { // FIXME: When we sort includes, we need to make sure the stream is correct // utf-8. size_t From = 0; size_t Index; while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) { outs() << Text.substr(From, Index - From); switch (Text[Index]) { case '\n': outs() << " "; break; case '\r': outs() << " "; break; case '<': outs() << "<"; break; case '&': outs() << "&"; break; default: llvm_unreachable("Unexpected character encountered!"); } From = Index + 1; } outs() << Text.substr(From); }
static bool ByteArrayFromString(ByteArrayTy &ByteArray, StringRef &Str, SourceMgr &SM) { while (SkipToToken(Str)) { // Handled by higher level if (Str[0] == '[' || Str[0] == ']') return false; // Get the current token. size_t Next = Str.find_first_of(" \t\n\r,#[]"); StringRef Value = Str.substr(0, Next); // Convert to a byte and add to the byte vector. unsigned ByteVal; if (Value.getAsInteger(0, ByteVal) || ByteVal > 255) { // If we have an error, print it and skip to the end of line. SM.PrintMessage(SMLoc::getFromPointer(Value.data()), SourceMgr::DK_Error, "invalid input token"); Str = Str.substr(Str.find('\n')); ByteArray.first.clear(); ByteArray.second.clear(); continue; } ByteArray.first.push_back(ByteVal); ByteArray.second.push_back(Value.data()); Str = Str.substr(Next); } return false; }
static bool ByteArrayFromString(ByteArrayTy &ByteArray, StringRef &Str, SourceMgr &SM) { while (!Str.empty()) { // Strip horizontal whitespace. if (size_t Pos = Str.find_first_not_of(" \t\r")) { Str = Str.substr(Pos); continue; } // If this is the end of a line or start of a comment, remove the rest of // the line. if (Str[0] == '\n' || Str[0] == '#') { // Strip to the end of line if we already processed any bytes on this // line. This strips the comment and/or the \n. if (Str[0] == '\n') { Str = Str.substr(1); } else { Str = Str.substr(Str.find_first_of('\n')); if (!Str.empty()) Str = Str.substr(1); } continue; } // Get the current token. size_t Next = Str.find_first_of(" \t\n\r#"); StringRef Value = Str.substr(0, Next); // Convert to a byte and add to the byte vector. unsigned ByteVal; if (Value.getAsInteger(0, ByteVal) || ByteVal > 255) { // If we have an error, print it and skip to the end of line. SM.PrintMessage(SMLoc::getFromPointer(Value.data()), SourceMgr::DK_Error, "invalid input token"); Str = Str.substr(Str.find('\n')); ByteArray.clear(); continue; } ByteArray.push_back(std::make_pair((unsigned char)ByteVal, Value.data())); Str = Str.substr(Next); } return false; }
// Returns a whole line containing the current token. StringRef ScriptParserBase::getLine() { StringRef S = getCurrentMB().getBuffer(); StringRef Tok = Tokens[Pos - 1]; size_t Pos = S.rfind('\n', Tok.data() - S.data()); if (Pos != StringRef::npos) S = S.substr(Pos + 1); return S.substr(0, S.find_first_of("\r\n")); }
VecString split( const char* chars, StringRef str ) { VecString vs; String::size_type n0 = 0; String::size_type n1 = str.find_first_of( chars, 0 ); vs.push_back( str.substr( n0, n1 ) ); while( n1 != String::npos ) { n0 = n1+1; n1 = str.find_first_of( chars, n0 ); if( n1 != String::npos ) vs.push_back( str.substr( n0, n1-n0 ) ); else vs.push_back( str.substr( n0 ) ); } return vs; }
// Primary pass pipeline description parsing routine. // FIXME: Should this routine accept a TargetMachine or require the caller to // pre-populate the analysis managers with target-specific stuff? bool PassBuilder::parsePassPipeline(ModulePassManager &MPM, StringRef PipelineText, bool VerifyEachPass, bool DebugLogging) { // By default, try to parse the pipeline as-if it were within an implicit // 'module(...)' pass pipeline. If this will parse at all, it needs to // consume the entire string. if (parseModulePassPipeline(MPM, PipelineText, VerifyEachPass, DebugLogging)) return PipelineText.empty(); // This isn't parsable as a module pipeline, look for the end of a pass name // and directly drop down to that layer. StringRef FirstName = PipelineText.substr(0, PipelineText.find_first_of(",)")); assert(!isModulePassName(FirstName) && "Already handled all module pipeline options."); // If this looks like a CGSCC pass, parse the whole thing as a CGSCC // pipeline. if (PipelineText.startswith("cgscc(") || isCGSCCPassName(FirstName)) { CGSCCPassManager CGPM(DebugLogging); if (!parseCGSCCPassPipeline(CGPM, PipelineText, VerifyEachPass, DebugLogging) || !PipelineText.empty()) return false; MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); return true; } // Similarly, if this looks like a Function pass, parse the whole thing as // a Function pipelien. if (PipelineText.startswith("function(") || isFunctionPassName(FirstName)) { FunctionPassManager FPM(DebugLogging); if (!parseFunctionPassPipeline(FPM, PipelineText, VerifyEachPass, DebugLogging) || !PipelineText.empty()) return false; MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); return true; } // If this looks like a Loop pass, parse the whole thing as a Loop pipeline. if (PipelineText.startswith("loop(") || isLoopPassName(FirstName)) { LoopPassManager LPM(DebugLogging); if (!parseLoopPassPipeline(LPM, PipelineText, VerifyEachPass, DebugLogging) || !PipelineText.empty()) return false; FunctionPassManager FPM(DebugLogging); FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM))); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); return true; } return false; }
Optional<std::vector<PassBuilder::PipelineElement>> PassBuilder::parsePipelineText(StringRef Text) { std::vector<PipelineElement> ResultPipeline; SmallVector<std::vector<PipelineElement> *, 4> PipelineStack = { &ResultPipeline}; for (;;) { std::vector<PipelineElement> &Pipeline = *PipelineStack.back(); size_t Pos = Text.find_first_of(",()"); Pipeline.push_back({Text.substr(0, Pos), {}}); // If we have a single terminating name, we're done. if (Pos == Text.npos) break; char Sep = Text[Pos]; Text = Text.substr(Pos + 1); if (Sep == ',') // Just a name ending in a comma, continue. continue; if (Sep == '(') { // Push the inner pipeline onto the stack to continue processing. PipelineStack.push_back(&Pipeline.back().InnerPipeline); continue; } assert(Sep == ')' && "Bogus separator!"); // When handling the close parenthesis, we greedily consume them to avoid // empty strings in the pipeline. do { // If we try to pop the outer pipeline we have unbalanced parentheses. if (PipelineStack.size() == 1) return None; PipelineStack.pop_back(); } while (Text.consume_front(")")); // Check if we've finished parsing. if (Text.empty()) break; // Otherwise, the end of an inner pipeline always has to be followed by // a comma, and then we can continue. if (!Text.consume_front(",")) return None; } if (PipelineStack.size() > 1) // Unbalanced paretheses. return None; assert(PipelineStack.back() == &ResultPipeline && "Wrong pipeline at the bottom of the stack!"); return {std::move(ResultPipeline)}; }
void Value::setNameImpl(const Twine &NewName) { // Fast-path: LLVMContext can be set to strip out non-GlobalValue names if (getContext().shouldDiscardValueNames() && !isa<GlobalValue>(this)) return; // Fast path for common IRBuilder case of setName("") when there is no name. if (NewName.isTriviallyEmpty() && !hasName()) return; SmallString<256> NameData; StringRef NameRef = NewName.toStringRef(NameData); assert(NameRef.find_first_of(0) == StringRef::npos && "Null bytes are not allowed in names"); // Name isn't changing? if (getName() == NameRef) return; assert(!getType()->isVoidTy() && "Cannot assign a name to void values!"); // Get the symbol table to update for this object. ValueSymbolTable *ST; if (getSymTab(this, ST)) return; // Cannot set a name on this value (e.g. constant). if (!ST) { // No symbol table to update? Just do the change. if (NameRef.empty()) { // Free the name for this value. destroyValueName(); return; } // NOTE: Could optimize for the case the name is shrinking to not deallocate // then reallocated. destroyValueName(); // Create the new name. setValueName(ValueName::Create(NameRef)); getValueName()->setValue(this); return; } // NOTE: Could optimize for the case the name is shrinking to not deallocate // then reallocated. if (hasName()) { // Remove old name. ST->removeValueName(getValueName()); destroyValueName(); if (NameRef.empty()) return; } // Name is changing to something new. setValueName(ST->createValueName(NameRef, this)); }
static SourceLoc findEndOfLine(SourceManager &SM, SourceLoc loc, unsigned bufferID) { CharSourceRange entireBuffer = SM.getRangeForBuffer(bufferID); CharSourceRange rangeFromLoc{SM, loc, entireBuffer.getEnd()}; StringRef textFromLoc = SM.extractText(rangeFromLoc); size_t newlineOffset = textFromLoc.find_first_of({"\r\n\0", 3}); if (newlineOffset == StringRef::npos) return entireBuffer.getEnd(); return loc.getAdvancedLoc(newlineOffset); }
/// \brief Guess the end-of-line sequence used in the given FileID. If the /// sequence can't be guessed return an Unix-style newline. static StringRef guessEOL(SourceManager &SM, FileID ID) { StringRef Content = SM.getBufferData(ID); StringRef Buffer = Content.substr(Content.find_first_of("\r\n")); return llvm::StringSwitch<StringRef>(Buffer) .StartsWith("\r\n", "\r\n") .StartsWith("\n\r", "\n\r") .StartsWith("\r", "\r") .Default("\n"); }
/// getToken - This function extracts one token from source, ignoring any /// leading characters that appear in the Delimiters string, and ending the /// token at any of the characters that appear in the Delimiters string. If /// there are no tokens in the source string, an empty string is returned. /// The function returns a pair containing the extracted token and the /// remaining tail string. std::pair<StringRef, StringRef> llvm::getToken(StringRef Source, StringRef Delimiters) { // Figure out where the token starts. StringRef::size_type Start = Source.find_first_not_of(Delimiters); // Find the next occurrence of the delimiter. StringRef::size_type End = Source.find_first_of(Delimiters, Start); return std::make_pair(Source.slice(Start, End), Source.substr(End)); }
static std::string QuoteProgramPathIfNeeded(StringRef Command) { if (Command.find_first_of(' ') == StringRef::npos) return Command; else { std::string ret; ret.reserve(Command.size() + 3); ret.push_back('"'); ret.append(Command.begin(), Command.end()); ret.push_back('"'); return ret; } }
bool PassBuilder::parseFunctionPassPipeline(FunctionPassManager &FPM, StringRef &PipelineText, bool VerifyEachPass, bool DebugLogging) { for (;;) { // Parse nested pass managers by recursing. if (PipelineText.startswith("function(")) { FunctionPassManager NestedFPM(DebugLogging); // Parse the inner pipeline inte the nested manager. PipelineText = PipelineText.substr(strlen("function(")); if (!parseFunctionPassPipeline(NestedFPM, PipelineText, VerifyEachPass, DebugLogging) || PipelineText.empty()) return false; assert(PipelineText[0] == ')'); PipelineText = PipelineText.substr(1); // Add the nested pass manager with the appropriate adaptor. FPM.addPass(std::move(NestedFPM)); } else if (PipelineText.startswith("loop(")) { LoopPassManager NestedLPM(DebugLogging); // Parse the inner pipeline inte the nested manager. PipelineText = PipelineText.substr(strlen("loop(")); if (!parseLoopPassPipeline(NestedLPM, PipelineText, VerifyEachPass, DebugLogging) || PipelineText.empty()) return false; assert(PipelineText[0] == ')'); PipelineText = PipelineText.substr(1); // Add the nested pass manager with the appropriate adaptor. FPM.addPass(createFunctionToLoopPassAdaptor(std::move(NestedLPM))); } else { // Otherwise try to parse a pass name. size_t End = PipelineText.find_first_of(",)"); if (!parseFunctionPassName(FPM, PipelineText.substr(0, End))) return false; if (VerifyEachPass) FPM.addPass(VerifierPass()); PipelineText = PipelineText.substr(End); } if (PipelineText.empty() || PipelineText[0] == ')') return true; assert(PipelineText[0] == ','); PipelineText = PipelineText.substr(1); } }
static bool parseCGSCCPassPipeline(CGSCCPassManager &CGPM, StringRef &PipelineText, bool VerifyEachPass, bool DebugLogging) { for (;;) { // Parse nested pass managers by recursing. if (PipelineText.startswith("cgscc(")) { CGSCCPassManager NestedCGPM(DebugLogging); // Parse the inner pipeline into the nested manager. PipelineText = PipelineText.substr(strlen("cgscc(")); if (!parseCGSCCPassPipeline(NestedCGPM, PipelineText, VerifyEachPass, DebugLogging) || PipelineText.empty()) return false; assert(PipelineText[0] == ')'); PipelineText = PipelineText.substr(1); // Add the nested pass manager with the appropriate adaptor. CGPM.addPass(std::move(NestedCGPM)); } else if (PipelineText.startswith("function(")) { FunctionPassManager NestedFPM(DebugLogging); // Parse the inner pipeline inte the nested manager. PipelineText = PipelineText.substr(strlen("function(")); if (!parseFunctionPassPipeline(NestedFPM, PipelineText, VerifyEachPass, DebugLogging) || PipelineText.empty()) return false; assert(PipelineText[0] == ')'); PipelineText = PipelineText.substr(1); // Add the nested pass manager with the appropriate adaptor. CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(NestedFPM))); } else { // Otherwise try to parse a pass name. size_t End = PipelineText.find_first_of(",)"); if (!parseCGSCCPassName(CGPM, PipelineText.substr(0, End))) return false; // FIXME: No verifier support for CGSCC passes! PipelineText = PipelineText.substr(End); } if (PipelineText.empty() || PipelineText[0] == ')') return true; assert(PipelineText[0] == ','); PipelineText = PipelineText.substr(1); } }
static bool parseModulePassPipeline(ModulePassManager &MPM, StringRef &PipelineText, bool VerifyEachPass) { for (;;) { // Parse nested pass managers by recursing. if (PipelineText.startswith("module(")) { ModulePassManager NestedMPM; // Parse the inner pipeline into the nested manager. PipelineText = PipelineText.substr(strlen("module(")); if (!parseModulePassPipeline(NestedMPM, PipelineText, VerifyEachPass) || PipelineText.empty()) return false; assert(PipelineText[0] == ')'); PipelineText = PipelineText.substr(1); // Now add the nested manager as a module pass. MPM.addPass(std::move(NestedMPM)); } else if (PipelineText.startswith("function(")) { FunctionPassManager NestedFPM; // Parse the inner pipeline inte the nested manager. PipelineText = PipelineText.substr(strlen("function(")); if (!parseFunctionPassPipeline(NestedFPM, PipelineText, VerifyEachPass) || PipelineText.empty()) return false; assert(PipelineText[0] == ')'); PipelineText = PipelineText.substr(1); // Add the nested pass manager with the appropriate adaptor. MPM.addPass(createModuleToFunctionPassAdaptor(std::move(NestedFPM))); } else { // Otherwise try to parse a pass name. size_t End = PipelineText.find_first_of(",)"); if (!parseModulePassName(MPM, PipelineText.substr(0, End))) return false; if (VerifyEachPass) MPM.addPass(VerifierPass()); PipelineText = PipelineText.substr(End); } if (PipelineText.empty() || PipelineText[0] == ')') return true; assert(PipelineText[0] == ','); PipelineText = PipelineText.substr(1); } }
/// CountNumNewlinesBetween - Count the number of newlines in the specified /// range. static unsigned CountNumNewlinesBetween(StringRef Range) { unsigned NumNewLines = 0; while (1) { // Scan for newline. Range = Range.substr(Range.find_first_of("\n\r")); if (Range.empty()) return NumNewLines; ++NumNewLines; // Handle \n\r and \r\n as a single newline. if (Range.size() > 1 && (Range[1] == '\n' || Range[1] == '\r') && (Range[0] != Range[1])) Range = Range.substr(1); Range = Range.substr(1); } }
// Append a #define line to Buf for Macro. Macro should be of the form XXX, // in which case we emit "#define XXX 1" or "XXX=Y z W" in which case we emit // "#define XXX Y z W". To get a #define with no value, use "XXX=". static void DefineBuiltinMacro(MacroBuilder &Builder, StringRef Macro, DiagnosticsEngine &Diags) { std::pair<StringRef, StringRef> MacroPair = Macro.split('='); StringRef MacroName = MacroPair.first; StringRef MacroBody = MacroPair.second; if (MacroName.size() != Macro.size()) { // Per GCC -D semantics, the macro ends at \n if it exists. StringRef::size_type End = MacroBody.find_first_of("\n\r"); if (End != StringRef::npos) Diags.Report(diag::warn_fe_macro_contains_embedded_newline) << MacroName; Builder.defineMacro(MacroName, MacroBody.substr(0, End)); } else { // Push "macroname 1". Builder.defineMacro(Macro); } }
// Find file from search paths. You can omit ".obj", this function takes // care of that. Note that the returned path is not guaranteed to exist. StringRef LinkerDriver::doFindFile(StringRef Filename) { bool hasPathSep = (Filename.find_first_of("/\\") != StringRef::npos); if (hasPathSep) return Filename; bool hasExt = (Filename.find('.') != StringRef::npos); for (StringRef Dir : SearchPaths) { SmallString<128> Path = Dir; llvm::sys::path::append(Path, Filename); if (llvm::sys::fs::exists(Path.str())) return Alloc.save(Path.str()); if (!hasExt) { Path.append(".obj"); if (llvm::sys::fs::exists(Path.str())) return Alloc.save(Path.str()); } } return Filename; }
static bool SkipToToken(StringRef &Str) { for (;;) { if (Str.empty()) return false; // Strip horizontal whitespace and commas. if (size_t Pos = Str.find_first_not_of(" \t\r\n,")) { Str = Str.substr(Pos); continue; } // If this is the start of a comment, remove the rest of the line. if (Str[0] == '#') { Str = Str.substr(Str.find_first_of('\n')); continue; } return true; } }
static void outputReplacementXML(StringRef Text) { size_t From = 0; size_t Index; while ((Index = Text.find_first_of("\n\r", From)) != StringRef::npos) { llvm::outs() << Text.substr(From, Index - From); switch (Text[Index]) { case '\n': llvm::outs() << " "; break; case '\r': llvm::outs() << " "; break; default: llvm_unreachable("Unexpected character encountered!"); } From = Index + 1; } llvm::outs() << Text.substr(From); }
/// \brief Find the end of the end of the directive, either the beginning of a /// newline or the end of file. // // \return The offset into the file where the directive ends along with a // boolean value indicating whether the directive ends because the end of file // was reached or not. static std::pair<unsigned, bool> findDirectiveEnd(SourceLocation HashLoc, SourceManager &SM, const LangOptions &LangOpts) { FileID FID = SM.getFileID(HashLoc); unsigned Offset = SM.getFileOffset(HashLoc); StringRef Content = SM.getBufferData(FID); Lexer Lex(SM.getLocForStartOfFile(FID), LangOpts, Content.begin(), Content.begin() + Offset, Content.end()); Lex.SetCommentRetentionState(true); Token Tok; // This loop look for the newline after our directive but avoids the ones part // of a multi-line comments: // // #include <foo> /* long \n comment */\n // ~~ no ~~ yes for (;;) { // find the beginning of the end-of-line sequence StringRef::size_type EOLOffset = Content.find_first_of("\r\n", Offset); // ends because EOF was reached if (EOLOffset == StringRef::npos) return std::make_pair(Content.size(), true); // find the token that contains our end-of-line unsigned TokEnd = 0; do { Lex.LexFromRawLexer(Tok); TokEnd = SM.getFileOffset(Tok.getLocation()) + Tok.getLength(); // happens when the whitespaces are eaten after a multiline comment if (Tok.is(tok::eof)) return std::make_pair(EOLOffset, false); } while (TokEnd < EOLOffset); // the end-of-line is not part of a multi-line comment, return its location if (Tok.isNot(tok::comment)) return std::make_pair(EOLOffset, false); // for the next search to start after the end of this token Offset = TokEnd; } }
size_t swift::ide::getOffsetOfLine(unsigned LineIndex, StringRef Text) { // SourceLoc start = SourceLoc(llvm::SMLoc::getFromPointer(Text.begin())); // FIXME: We should have a cached line map in EditableTextBuffer, for now // we just do the slow naive thing here. size_t LineOffset = 0; unsigned CurrentLine = 0; while (LineOffset < Text.size() && ++CurrentLine < LineIndex) { LineOffset = Text.find_first_of("\r\n", LineOffset); if (LineOffset != std::string::npos) { ++LineOffset; if (LineOffset < Text.size() && Text[LineOffset - 1] == '\r' && Text[LineOffset] == '\n') ++LineOffset; } } if (LineOffset == std::string::npos) LineOffset = 0; return LineOffset; }
void MCMachObjectSymbolizer:: tryAddingPcLoadReferenceComment(raw_ostream &cStream, int64_t Value, uint64_t Address) { if (const RelocationRef *R = findRelocationAt(Address)) { const MCExpr *RelExpr = RelInfo->createExprForRelocation(*R); if (!RelExpr || RelExpr->EvaluateAsAbsolute(Value) == false) return; } uint64_t Addr = Value; if (const SectionRef *S = findSectionContaining(Addr)) { StringRef Name; S->getName(Name); uint64_t SAddr; S->getAddress(SAddr); if (Name == "__cstring") { StringRef Contents; S->getContents(Contents); Contents = Contents.substr(Addr - SAddr); cStream << " ## literal pool for: " << Contents.substr(0, Contents.find_first_of(0)); } } }
void swift::trimLeadingWhitespaceFromLines(StringRef RawText, unsigned WhitespaceToTrim, SmallVectorImpl<StringRef> &OutLines) { SmallVector<StringRef, 8> Lines; bool IsFirstLine = true; while (!RawText.empty()) { size_t Pos = RawText.find_first_of("\n\r"); if (Pos == StringRef::npos) Pos = RawText.size(); StringRef Line = RawText.substr(0, Pos); Lines.push_back(Line); if (!IsFirstLine) { size_t NonWhitespacePos = RawText.find_first_not_of(' '); if (NonWhitespacePos != StringRef::npos) WhitespaceToTrim = std::min(WhitespaceToTrim, static_cast<unsigned>(NonWhitespacePos)); } IsFirstLine = false; RawText = RawText.drop_front(Pos); unsigned NewlineBytes = measureNewline(RawText); RawText = RawText.drop_front(NewlineBytes); } IsFirstLine = true; for (auto &Line : Lines) { if (!IsFirstLine) { Line = Line.drop_front(WhitespaceToTrim); } IsFirstLine = false; } OutLines.append(Lines.begin(), Lines.end()); }
bool PassBuilder::parseLoopPassPipeline(LoopPassManager &LPM, StringRef &PipelineText, bool VerifyEachPass, bool DebugLogging) { for (;;) { // Parse nested pass managers by recursing. if (PipelineText.startswith("loop(")) { LoopPassManager NestedLPM(DebugLogging); // Parse the inner pipeline inte the nested manager. PipelineText = PipelineText.substr(strlen("loop(")); if (!parseLoopPassPipeline(NestedLPM, PipelineText, VerifyEachPass, DebugLogging) || PipelineText.empty()) return false; assert(PipelineText[0] == ')'); PipelineText = PipelineText.substr(1); // Add the nested pass manager with the appropriate adaptor. LPM.addPass(std::move(NestedLPM)); } else { // Otherwise try to parse a pass name. size_t End = PipelineText.find_first_of(",)"); if (!parseLoopPassName(LPM, PipelineText.substr(0, End))) return false; // TODO: Ideally, we would run a LoopVerifierPass() here in the // VerifyEachPass case, but we don't have such a verifier yet. PipelineText = PipelineText.substr(End); } if (PipelineText.empty() || PipelineText[0] == ')') return true; assert(PipelineText[0] == ','); PipelineText = PipelineText.substr(1); } }
// Append a #define line to Buf for Macro. Macro should be of the form XXX, // in which case we emit "#define XXX 1" or "XXX=Y z W" in which case we emit // "#define XXX Y z W". To get a #define with no value, use "XXX=". static void DefineBuiltinMacro(MacroBuilder &Builder, StringRef Macro, DiagnosticsEngine &Diags) { std::pair<StringRef, StringRef> MacroPair = Macro.split('='); StringRef MacroName = MacroPair.first; StringRef MacroBody = MacroPair.second; if (MacroName.size() != Macro.size()) { // Per GCC -D semantics, the macro ends at \n if it exists. StringRef::size_type End = MacroBody.find_first_of("\n\r"); if (End != StringRef::npos) Diags.Report(diag::warn_fe_macro_contains_embedded_newline) << MacroName; MacroBody = MacroBody.substr(0, End); // We handle macro bodies which end in a backslash by appending an extra // backslash+newline. This makes sure we don't accidentally treat the // backslash as a line continuation marker. if (MacroBodyEndsInBackslash(MacroBody)) Builder.defineMacro(MacroName, Twine(MacroBody) + "\\\n"); else Builder.defineMacro(MacroName, MacroBody); } else { // Push "macroname 1". Builder.defineMacro(Macro); } }
/// \brief After the file has been processed, check to see if we got all of /// the expected diagnostics and check to see if there were any unexpected /// ones. bool DiagnosticVerifier::verifyFile(unsigned BufferID, bool shouldAutoApplyFixes) { using llvm::SMLoc; const SourceLoc BufferStartLoc = SM.getLocForBufferStart(BufferID); CharSourceRange EntireRange = SM.getRangeForBuffer(BufferID); StringRef InputFile = SM.extractText(EntireRange); StringRef BufferName = SM.getIdentifierForBuffer(BufferID); // Queue up all of the diagnostics, allowing us to sort them and emit them in // file order. std::vector<llvm::SMDiagnostic> Errors; unsigned PrevExpectedContinuationLine = 0; std::vector<ExpectedDiagnosticInfo> ExpectedDiagnostics; auto addError = [&](const char *Loc, std::string message, ArrayRef<llvm::SMFixIt> FixIts = {}) { auto loc = SourceLoc(SMLoc::getFromPointer(Loc)); auto diag = SM.GetMessage(loc, llvm::SourceMgr::DK_Error, message, {}, FixIts); Errors.push_back(diag); }; // Scan the memory buffer looking for expected-note/warning/error. for (size_t Match = InputFile.find("expected-"); Match != StringRef::npos; Match = InputFile.find("expected-", Match+1)) { // Process this potential match. If we fail to process it, just move on to // the next match. StringRef MatchStart = InputFile.substr(Match); const char *DiagnosticLoc = MatchStart.data(); llvm::SourceMgr::DiagKind ExpectedClassification; if (MatchStart.startswith("expected-note")) { ExpectedClassification = llvm::SourceMgr::DK_Note; MatchStart = MatchStart.substr(strlen("expected-note")); } else if (MatchStart.startswith("expected-warning")) { ExpectedClassification = llvm::SourceMgr::DK_Warning; MatchStart = MatchStart.substr(strlen("expected-warning")); } else if (MatchStart.startswith("expected-error")) { ExpectedClassification = llvm::SourceMgr::DK_Error; MatchStart = MatchStart.substr(strlen("expected-error")); } else continue; // Skip any whitespace before the {{. MatchStart = MatchStart.substr(MatchStart.find_first_not_of(" \t")); size_t TextStartIdx = MatchStart.find("{{"); if (TextStartIdx == StringRef::npos) { addError(MatchStart.data(), "expected {{ in expected-warning/note/error line"); continue; } int LineOffset = 0; if (TextStartIdx > 0 && MatchStart[0] == '@') { if (MatchStart[1] != '+' && MatchStart[1] != '-') { addError(MatchStart.data(), "expected '+'/'-' for line offset"); continue; } StringRef Offs; if (MatchStart[1] == '+') Offs = MatchStart.slice(2, TextStartIdx).rtrim(); else Offs = MatchStart.slice(1, TextStartIdx).rtrim(); size_t SpaceIndex = Offs.find(' '); if (SpaceIndex != StringRef::npos && SpaceIndex < TextStartIdx) { size_t Delta = Offs.size() - SpaceIndex; MatchStart = MatchStart.substr(TextStartIdx - Delta); TextStartIdx = Delta; Offs = Offs.slice(0, SpaceIndex); } else { MatchStart = MatchStart.substr(TextStartIdx); TextStartIdx = 0; } if (Offs.getAsInteger(10, LineOffset)) { addError(MatchStart.data(), "expected line offset before '{{'"); continue; } } ExpectedDiagnosticInfo Expected(DiagnosticLoc, ExpectedClassification); unsigned Count = 1; if (TextStartIdx > 0) { StringRef CountStr = MatchStart.substr(0, TextStartIdx).trim(); if (CountStr == "*") { Expected.mayAppear = true; } else { if (CountStr.getAsInteger(10, Count)) { addError(MatchStart.data(), "expected match count before '{{'"); continue; } if (Count == 0) { addError(MatchStart.data(), "expected positive match count before '{{'"); continue; } } // Resync up to the '{{'. MatchStart = MatchStart.substr(TextStartIdx); } size_t End = MatchStart.find("}}"); if (End == StringRef::npos) { addError(MatchStart.data(), "didn't find '}}' to match '{{' in expected-warning/note/error line"); continue; } llvm::SmallString<256> Buf; Expected.MessageRange = MatchStart.slice(2, End); Expected.MessageStr = Lexer::getEncodedStringSegment(Expected.MessageRange, Buf); if (PrevExpectedContinuationLine) Expected.LineNo = PrevExpectedContinuationLine; else Expected.LineNo = SM.getLineAndColumn( BufferStartLoc.getAdvancedLoc(MatchStart.data() - InputFile.data()), BufferID).first; Expected.LineNo += LineOffset; // Check if the next expected diagnostic should be in the same line. StringRef AfterEnd = MatchStart.substr(End + strlen("}}")); AfterEnd = AfterEnd.substr(AfterEnd.find_first_not_of(" \t")); if (AfterEnd.startswith("\\")) PrevExpectedContinuationLine = Expected.LineNo; else PrevExpectedContinuationLine = 0; // Scan for fix-its: {{10-14=replacement text}} StringRef ExtraChecks = MatchStart.substr(End+2).ltrim(" \t"); while (ExtraChecks.startswith("{{")) { // First make sure we have a closing "}}". size_t EndLoc = ExtraChecks.find("}}"); if (EndLoc == StringRef::npos) { addError(ExtraChecks.data(), "didn't find '}}' to match '{{' in fix-it verification"); break; } // Allow for close braces to appear in the replacement text. while (EndLoc+2 < ExtraChecks.size() && ExtraChecks[EndLoc+2] == '}') ++EndLoc; StringRef FixItStr = ExtraChecks.slice(2, EndLoc); // Check for matching a later "}}" on a different line. if (FixItStr.find_first_of("\r\n") != StringRef::npos) { addError(ExtraChecks.data(), "didn't find '}}' to match '{{' in " "fix-it verification"); break; } // Prepare for the next round of checks. ExtraChecks = ExtraChecks.substr(EndLoc+2).ltrim(); // Special case for specifying no fixits should appear. if (FixItStr == "none") { Expected.noFixitsMayAppear = true; continue; } // Parse the pieces of the fix-it. size_t MinusLoc = FixItStr.find('-'); if (MinusLoc == StringRef::npos) { addError(FixItStr.data(), "expected '-' in fix-it verification"); continue; } StringRef StartColStr = FixItStr.slice(0, MinusLoc); StringRef AfterMinus = FixItStr.substr(MinusLoc+1); size_t EqualLoc = AfterMinus.find('='); if (EqualLoc == StringRef::npos) { addError(AfterMinus.data(), "expected '=' after '-' in fix-it verification"); continue; } StringRef EndColStr = AfterMinus.slice(0, EqualLoc); StringRef AfterEqual = AfterMinus.substr(EqualLoc+1); ExpectedFixIt FixIt; FixIt.StartLoc = StartColStr.data()-2; FixIt.EndLoc = FixItStr.data()+EndLoc; if (StartColStr.getAsInteger(10, FixIt.StartCol)) { addError(StartColStr.data(), "invalid column number in fix-it verification"); continue; } if (EndColStr.getAsInteger(10, FixIt.EndCol)) { addError(EndColStr.data(), "invalid column number in fix-it verification"); continue; } // Translate literal "\\n" into '\n', inefficiently. StringRef fixItText = AfterEqual.slice(0, EndLoc); for (const char *current = fixItText.begin(), *end = fixItText.end(); current != end; /* in loop */) { if (*current == '\\' && current + 1 < end) { if (current[1] == 'n') { FixIt.Text += '\n'; current += 2; } else { // Handle \}, \\, etc. FixIt.Text += current[1]; current += 2; } } else { FixIt.Text += *current++; } } Expected.Fixits.push_back(FixIt); } Expected.ExpectedEnd = ExtraChecks.data(); // Don't include trailing whitespace in the expected-foo{{}} range. while (isspace(Expected.ExpectedEnd[-1])) --Expected.ExpectedEnd; // Add the diagnostic the expected number of times. for (; Count; --Count) ExpectedDiagnostics.push_back(Expected); } // Make sure all the expected diagnostics appeared. std::reverse(ExpectedDiagnostics.begin(), ExpectedDiagnostics.end()); for (unsigned i = ExpectedDiagnostics.size(); i != 0; ) { --i; auto &expected = ExpectedDiagnostics[i]; // Check to see if we had this expected diagnostic. auto FoundDiagnosticIter = findDiagnostic(expected, BufferName); if (FoundDiagnosticIter == CapturedDiagnostics.end()) { // Diagnostic didn't exist. If this is a 'mayAppear' diagnostic, then // we're ok. Otherwise, leave it in the list. if (expected.mayAppear) ExpectedDiagnostics.erase(ExpectedDiagnostics.begin()+i); continue; } auto &FoundDiagnostic = *FoundDiagnosticIter; const char *IncorrectFixit = nullptr; // Verify that any expected fix-its are present in the diagnostic. for (auto fixit : expected.Fixits) { // If we found it, we're ok. if (!checkForFixIt(fixit, FoundDiagnostic, InputFile)) IncorrectFixit = fixit.StartLoc; } // If we have any expected fixits that didn't get matched, then they are // wrong. Replace the failed fixit with what actually happened. if (IncorrectFixit) { if (FoundDiagnostic.getFixIts().empty()) { addError(IncorrectFixit, "expected fix-it not seen"); continue; } // If we had an incorrect expected fixit, render it and produce a fixit // of our own. auto actual = renderFixits(FoundDiagnostic.getFixIts(), InputFile); auto replStartLoc = SMLoc::getFromPointer(expected.Fixits[0].StartLoc); auto replEndLoc = SMLoc::getFromPointer(expected.Fixits.back().EndLoc); llvm::SMFixIt fix(llvm::SMRange(replStartLoc, replEndLoc), actual); addError(IncorrectFixit, "expected fix-it not seen; actual fix-its: " + actual, fix); } else if (expected.noFixitsMayAppear && !FoundDiagnostic.getFixIts().empty() && !expected.mayAppear) { // If there was no fixit specification, but some were produced, add a // fixit to add them in. auto actual = renderFixits(FoundDiagnostic.getFixIts(), InputFile); auto replStartLoc = SMLoc::getFromPointer(expected.ExpectedEnd - 8); // {{none}} length auto replEndLoc = SMLoc::getFromPointer(expected.ExpectedEnd - 1); llvm::SMFixIt fix(llvm::SMRange(replStartLoc, replEndLoc), actual); addError(replStartLoc.getPointer(), "expected no fix-its; actual fix-it seen: " + actual, fix); } // Actually remove the diagnostic from the list, so we don't match it // again. We do have to do this after checking fix-its, though, because // the diagnostic owns its fix-its. CapturedDiagnostics.erase(FoundDiagnosticIter); // We found the diagnostic, so remove it... unless we allow an arbitrary // number of diagnostics, in which case we want to reprocess this. if (expected.mayAppear) ++i; else ExpectedDiagnostics.erase(ExpectedDiagnostics.begin()+i); } // Check to see if we have any incorrect diagnostics. If so, diagnose them as // such. for (unsigned i = ExpectedDiagnostics.size(); i != 0; ) { --i; auto &expected = ExpectedDiagnostics[i]; // Check to see if any found diagnostics have the right line and // classification, but the wrong text. auto I = CapturedDiagnostics.begin(); for (auto E = CapturedDiagnostics.end(); I != E; ++I) { // Verify the file and line of the diagnostic. if (I->getLineNo() != (int)expected.LineNo || I->getFilename() != BufferName || I->getKind() != expected.Classification) continue; // Otherwise, we found it, break out. break; } if (I == CapturedDiagnostics.end()) continue; auto StartLoc = SMLoc::getFromPointer(expected.MessageRange.begin()); auto EndLoc = SMLoc::getFromPointer(expected.MessageRange.end()); llvm::SMFixIt fixIt(llvm::SMRange{ StartLoc, EndLoc }, I->getMessage()); addError(expected.MessageRange.begin(), "incorrect message found", fixIt); CapturedDiagnostics.erase(I); ExpectedDiagnostics.erase(ExpectedDiagnostics.begin()+i); } // Diagnose expected diagnostics that didn't appear. std::reverse(ExpectedDiagnostics.begin(), ExpectedDiagnostics.end()); for (auto const &expected : ExpectedDiagnostics) { std::string message = "expected "+getDiagKindString(expected.Classification) + " not produced"; // Get the range of the expected-foo{{}} diagnostic specifier. auto StartLoc = expected.ExpectedStart; auto EndLoc = expected.ExpectedEnd; // A very common case if for the specifier to be the last thing on the line. // In this case, eat any trailing whitespace. while (isspace(*EndLoc) && *EndLoc != '\n' && *EndLoc != '\r') ++EndLoc; // If we found the end of the line, we can do great things. Otherwise, // avoid nuking whitespace that might be zapped through other means. if (*EndLoc != '\n' && *EndLoc != '\r') { EndLoc = expected.ExpectedEnd; } else { // If we hit the end of line, then zap whitespace leading up to it. auto FileStart = InputFile.data(); while (StartLoc-1 != FileStart && isspace(StartLoc[-1]) && StartLoc[-1] != '\n' && StartLoc[-1] != '\r') --StartLoc; // If we got to the end of the line, and the thing before this diagnostic // is a "//" then we can remove it too. if (StartLoc-2 >= FileStart && StartLoc[-1] == '/' && StartLoc[-2] == '/') StartLoc -= 2; // Perform another round of general whitespace nuking to cleanup // whitespace before the //. while (StartLoc-1 != FileStart && isspace(StartLoc[-1]) && StartLoc[-1] != '\n' && StartLoc[-1] != '\r') --StartLoc; // If we found a \n, then we can nuke the entire line. if (StartLoc-1 != FileStart && (StartLoc[-1] == '\n' || StartLoc[-1] == '\r')) --StartLoc; } // Remove the expected-foo{{}} as a fixit. llvm::SMFixIt fixIt(llvm::SMRange{ SMLoc::getFromPointer(StartLoc), SMLoc::getFromPointer(EndLoc) }, ""); addError(expected.ExpectedStart, message, fixIt); } // Verify that there are no diagnostics (in MemoryBuffer) left in the list. for (unsigned i = 0, e = CapturedDiagnostics.size(); i != e; ++i) { if (CapturedDiagnostics[i].getFilename() != BufferName) continue; std::string Message = "unexpected "+getDiagKindString(CapturedDiagnostics[i].getKind())+ " produced: "+CapturedDiagnostics[i].getMessage().str(); addError(CapturedDiagnostics[i].getLoc().getPointer(), Message); } // Sort the diagnostics by their address in the memory buffer as the primary // key. This ensures that an "unexpected diagnostic" and // "expected diagnostic" in the same place are emitted next to each other. std::sort(Errors.begin(), Errors.end(), [&](const llvm::SMDiagnostic &lhs, const llvm::SMDiagnostic &rhs) -> bool { return lhs.getLoc().getPointer() < rhs.getLoc().getPointer(); }); // Emit all of the queue'd up errors. for (auto Err : Errors) SM.getLLVMSourceMgr().PrintMessage(llvm::errs(), Err); // If auto-apply fixits is on, rewrite the original source file. if (shouldAutoApplyFixes) autoApplyFixes(BufferID, Errors); return !Errors.empty(); }