bool WinEHStatePass::runOnFunction(Function &F) { // If this is an outlined handler, don't do anything. We'll do state insertion // for it in the parent. StringRef WinEHParentName = F.getFnAttribute("wineh-parent").getValueAsString(); if (WinEHParentName != F.getName() && !WinEHParentName.empty()) return false; // Check the personality. Do nothing if this is not an MSVC personality. if (!F.hasPersonalityFn()) return false; PersonalityFn = dyn_cast<Function>(F.getPersonalityFn()->stripPointerCasts()); if (!PersonalityFn) return false; Personality = classifyEHPersonality(PersonalityFn); if (!isMSVCEHPersonality(Personality)) return false; // Skip this function if there are no EH pads and we aren't using IR-level // outlining. if (WinEHParentName.empty()) { bool HasPads = false; for (BasicBlock &BB : F) { if (BB.isEHPad()) { HasPads = true; break; } } if (!HasPads) return false; } // Disable frame pointer elimination in this function. // FIXME: Do the nested handlers need to keep the parent ebp in ebp, or can we // use an arbitrary register? F.addFnAttr("no-frame-pointer-elim", "true"); emitExceptionRegistrationRecord(&F); auto *MMI = getAnalysisIfAvailable<MachineModuleInfo>(); // If MMI is null, create our own WinEHFuncInfo. This only happens in opt // tests. std::unique_ptr<WinEHFuncInfo> FuncInfoPtr; if (!MMI) FuncInfoPtr.reset(new WinEHFuncInfo()); WinEHFuncInfo &FuncInfo = *(MMI ? &MMI->getWinEHFuncInfo(&F) : FuncInfoPtr.get()); FuncInfo.EHRegNode = RegNode; switch (Personality) { default: llvm_unreachable("unexpected personality function"); case EHPersonality::MSVC_CXX: addCXXStateStores(F, FuncInfo); break; case EHPersonality::MSVC_X86SEH: addSEHStateStores(F, FuncInfo); break; } // Reset per-function state. PersonalityFn = nullptr; Personality = EHPersonality::Unknown; return true; }
/// Run the LLVM passes. In multi-threaded compilation this will be done for /// multiple LLVM modules in parallel. static bool performLLVM(IRGenOptions &Opts, DiagnosticEngine &Diags, llvm::sys::Mutex *DiagMutex, llvm::Module *Module, llvm::TargetMachine *TargetMachine, StringRef OutputFilename) { llvm::SmallString<0> Buffer; std::unique_ptr<raw_pwrite_stream> RawOS; if (!OutputFilename.empty()) { // Try to open the output file. Clobbering an existing file is fine. // Open in binary mode if we're doing binary output. llvm::sys::fs::OpenFlags OSFlags = llvm::sys::fs::F_None; std::error_code EC; auto *FDOS = new raw_fd_ostream(OutputFilename, EC, OSFlags); RawOS.reset(FDOS); if (FDOS->has_error() || EC) { if (DiagMutex) DiagMutex->lock(); Diags.diagnose(SourceLoc(), diag::error_opening_output, OutputFilename, EC.message()); if (DiagMutex) DiagMutex->unlock(); FDOS->clear_error(); return true; } // Most output kinds want a formatted output stream. It's not clear // why writing an object file does. //if (Opts.OutputKind != IRGenOutputKind::LLVMBitcode) // FormattedOS.setStream(*RawOS, formatted_raw_ostream::PRESERVE_STREAM); } else { RawOS.reset(new raw_svector_ostream(Buffer)); } performLLVMOptimizations(Opts, Module, TargetMachine); legacy::PassManager EmitPasses; // Set up the final emission passes. switch (Opts.OutputKind) { case IRGenOutputKind::Module: break; case IRGenOutputKind::LLVMAssembly: EmitPasses.add(createPrintModulePass(*RawOS)); break; case IRGenOutputKind::LLVMBitcode: EmitPasses.add(createBitcodeWriterPass(*RawOS)); break; case IRGenOutputKind::NativeAssembly: case IRGenOutputKind::ObjectFile: { llvm::TargetMachine::CodeGenFileType FileType; FileType = (Opts.OutputKind == IRGenOutputKind::NativeAssembly ? llvm::TargetMachine::CGFT_AssemblyFile : llvm::TargetMachine::CGFT_ObjectFile); EmitPasses.add(createTargetTransformInfoWrapperPass( TargetMachine->getTargetIRAnalysis())); // Make sure we do ARC contraction under optimization. We don't // rely on any other LLVM ARC transformations, but we do need ARC // contraction to add the objc_retainAutoreleasedReturnValue // assembly markers. if (Opts.Optimize) EmitPasses.add(createObjCARCContractPass()); bool fail = TargetMachine->addPassesToEmitFile(EmitPasses, *RawOS, FileType, !Opts.Verify); if (fail) { if (DiagMutex) DiagMutex->lock(); Diags.diagnose(SourceLoc(), diag::error_codegen_init_fail); if (DiagMutex) DiagMutex->unlock(); return true; } break; } } { SharedTimer timer("LLVM output"); EmitPasses.run(*Module); } return false; }
// Implementation of StringRef hashing. hash_code llvm::hash_value(StringRef S) { return hash_combine_range(S.begin(), S.end()); }
Archive::Archive(MemoryBufferRef Source, Error &Err) : Binary(Binary::ID_Archive, Source) { ErrorAsOutParameter ErrAsOutParam(&Err); StringRef Buffer = Data.getBuffer(); // Check for sufficient magic. if (Buffer.startswith(ThinMagic)) { IsThin = true; } else if (Buffer.startswith(Magic)) { IsThin = false; } else { Err = make_error<GenericBinaryError>("File too small to be an archive", object_error::invalid_file_type); return; } // Get the special members. child_iterator I = child_begin(Err, false); if (Err) return; child_iterator E = child_end(); // This is at least a valid empty archive. Since an empty archive is the // same in all formats, just claim it to be gnu to make sure Format is // initialized. Format = K_GNU; if (I == E) { Err = Error::success(); return; } const Child *C = &*I; auto Increment = [&]() { ++I; if (Err) return true; C = &*I; return false; }; StringRef Name = C->getRawName(); // Below is the pattern that is used to figure out the archive format // GNU archive format // First member : / (may exist, if it exists, points to the symbol table ) // Second member : // (may exist, if it exists, points to the string table) // Note : The string table is used if the filename exceeds 15 characters // BSD archive format // First member : __.SYMDEF or "__.SYMDEF SORTED" (the symbol table) // There is no string table, if the filename exceeds 15 characters or has a // embedded space, the filename has #1/<size>, The size represents the size // of the filename that needs to be read after the archive header // COFF archive format // First member : / // Second member : / (provides a directory of symbols) // Third member : // (may exist, if it exists, contains the string table) // Note: Microsoft PE/COFF Spec 8.3 says that the third member is present // even if the string table is empty. However, lib.exe does not in fact // seem to create the third member if there's no member whose filename // exceeds 15 characters. So the third member is optional. if (Name == "__.SYMDEF" || Name == "__.SYMDEF_64") { if (Name == "__.SYMDEF") Format = K_BSD; else // Name == "__.SYMDEF_64" Format = K_DARWIN64; // We know that the symbol table is not an external file, so we just assert // there is no error. SymbolTable = *C->getBuffer(); if (Increment()) return; setFirstRegular(*C); Err = Error::success(); return; } if (Name.startswith("#1/")) { Format = K_BSD; // We know this is BSD, so getName will work since there is no string table. ErrorOr<StringRef> NameOrErr = C->getName(); if (auto ec = NameOrErr.getError()) { Err = errorCodeToError(ec); return; } Name = NameOrErr.get(); if (Name == "__.SYMDEF SORTED" || Name == "__.SYMDEF") { // We know that the symbol table is not an external file, so we just // assert there is no error. SymbolTable = *C->getBuffer(); if (Increment()) return; } else if (Name == "__.SYMDEF_64 SORTED" || Name == "__.SYMDEF_64") { Format = K_DARWIN64; // We know that the symbol table is not an external file, so we just // assert there is no error. SymbolTable = *C->getBuffer(); if (Increment()) return; } setFirstRegular(*C); return; } // MIPS 64-bit ELF archives use a special format of a symbol table. // This format is marked by `ar_name` field equals to "/SYM64/". // For detailed description see page 96 in the following document: // http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf bool has64SymTable = false; if (Name == "/" || Name == "/SYM64/") { // We know that the symbol table is not an external file, so we just assert // there is no error. SymbolTable = *C->getBuffer(); if (Name == "/SYM64/") has64SymTable = true; if (Increment()) return; if (I == E) { Err = Error::success(); return; } Name = C->getRawName(); } if (Name == "//") { Format = has64SymTable ? K_MIPS64 : K_GNU; // The string table is never an external member, so we just assert on the // ErrorOr. StringTable = *C->getBuffer(); if (Increment()) return; setFirstRegular(*C); Err = Error::success(); return; } if (Name[0] != '/') { Format = has64SymTable ? K_MIPS64 : K_GNU; setFirstRegular(*C); Err = Error::success(); return; } if (Name != "/") { Err = errorCodeToError(object_error::parse_failed); return; } Format = K_COFF; // We know that the symbol table is not an external file, so we just assert // there is no error. SymbolTable = *C->getBuffer(); if (Increment()) return; if (I == E) { setFirstRegular(*C); Err = Error::success(); return; } Name = C->getRawName(); if (Name == "//") { // The string table is never an external member, so we just assert on the // ErrorOr. StringTable = *C->getBuffer(); if (Increment()) return; } setFirstRegular(*C); Err = Error::success(); }
void Value::setName(const Twine &NewName) { assert(SubclassID != MDStringVal && "Cannot set the name of MDString with this method!"); // 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 (Function *F = dyn_cast<Function>(this)) getContext().pImpl->IntrinsicIDCache.erase(F); if (!ST) { // No symbol table to update? Just do the change. if (NameRef.empty()) { // Free the name for this value. Name->Destroy(); Name = nullptr; return; } if (Name) Name->Destroy(); // NOTE: Could optimize for the case the name is shrinking to not deallocate // then reallocated. // Create the new name. Name = ValueName::Create(NameRef); Name->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(Name); Name->Destroy(); Name = nullptr; if (NameRef.empty()) return; } // Name is changing to something new. Name = ST->createValueName(NameRef, this); }
/// Determine the prefix to be stripped from the names of the enum constants /// within the given enum. void EnumInfo::determineConstantNamePrefix(ASTContext &ctx, const clang::EnumDecl *decl) { switch (getKind()) { case EnumKind::Enum: case EnumKind::Options: // Enums are mapped to Swift enums, Options to Swift option sets, both // of which attempt prefix-stripping. break; case EnumKind::Constants: case EnumKind::Unknown: // Nothing to do. return; } // If there are no enumers, there is no prefix to compute. auto ec = decl->enumerator_begin(), ecEnd = decl->enumerator_end(); if (ec == ecEnd) return; // Determine whether the given enumerator is non-deprecated and has no // specifically-provided name. auto isNonDeprecatedWithoutCustomName = []( const clang::EnumConstantDecl *elem) -> bool { if (elem->hasAttr<clang::SwiftNameAttr>()) return false; clang::VersionTuple maxVersion{~0U, ~0U, ~0U}; switch (elem->getAvailability(nullptr, maxVersion)) { case clang::AR_Available: case clang::AR_NotYetIntroduced: for (auto attr : elem->attrs()) { if (auto annotate = dyn_cast<clang::AnnotateAttr>(attr)) { if (annotate->getAnnotation() == "swift1_unavailable") return false; } if (auto avail = dyn_cast<clang::AvailabilityAttr>(attr)) { if (avail->getPlatform()->getName() == "swift") return false; } } return true; case clang::AR_Deprecated: case clang::AR_Unavailable: return false; } }; // Move to the first non-deprecated enumerator, or non-swift_name'd // enumerator, if present. auto firstNonDeprecated = std::find_if(ec, ecEnd, isNonDeprecatedWithoutCustomName); bool hasNonDeprecated = (firstNonDeprecated != ecEnd); if (hasNonDeprecated) { ec = firstNonDeprecated; } else { // Advance to the first case without a custom name, deprecated or not. while (ec != ecEnd && (*ec)->hasAttr<clang::SwiftNameAttr>()) ++ec; if (ec == ecEnd) { return; } } // Compute the common prefix. StringRef commonPrefix = (*ec)->getName(); bool followedByNonIdentifier = false; for (++ec; ec != ecEnd; ++ec) { // Skip deprecated or swift_name'd enumerators. const clang::EnumConstantDecl *elem = *ec; if (hasNonDeprecated) { if (!isNonDeprecatedWithoutCustomName(elem)) continue; } else { if (elem->hasAttr<clang::SwiftNameAttr>()) continue; } commonPrefix = getCommonWordPrefix(commonPrefix, elem->getName(), followedByNonIdentifier); if (commonPrefix.empty()) break; } if (!commonPrefix.empty()) { StringRef checkPrefix = commonPrefix; // Account for the 'kConstant' naming convention on enumerators. if (checkPrefix[0] == 'k') { bool canDropK; if (checkPrefix.size() >= 2) canDropK = clang::isUppercase(checkPrefix[1]); else canDropK = !followedByNonIdentifier; if (canDropK) checkPrefix = checkPrefix.drop_front(); } // Don't use importFullName() here, we want to ignore the swift_name // and swift_private attributes. StringRef enumNameStr = decl->getName(); StringRef commonWithEnum = getCommonPluralPrefix(checkPrefix, enumNameStr); size_t delta = commonPrefix.size() - checkPrefix.size(); // Account for the 'EnumName_Constant' convention on enumerators. if (commonWithEnum.size() < checkPrefix.size() && checkPrefix[commonWithEnum.size()] == '_' && !followedByNonIdentifier) { delta += 1; } commonPrefix = commonPrefix.slice(0, commonWithEnum.size() + delta); } constantNamePrefix = ctx.AllocateCopy(commonPrefix); }
Archive::Child::Child(const Archive *Parent, StringRef Data, uint16_t StartOfFile) : Parent(Parent), Header(Parent, Data.data(), Data.size(), nullptr), Data(Data), StartOfFile(StartOfFile) { }
// Load single header list and dependencies. std::error_code ModularizeUtilities::loadSingleHeaderListsAndDependencies( llvm::StringRef InputPath) { // By default, use the path component of the list file name. SmallString<256> HeaderDirectory(InputPath); llvm::sys::path::remove_filename(HeaderDirectory); SmallString<256> CurrentDirectory; llvm::sys::fs::current_path(CurrentDirectory); // Get the prefix if we have one. if (HeaderPrefix.size() != 0) HeaderDirectory = HeaderPrefix; // Read the header list file into a buffer. ErrorOr<std::unique_ptr<MemoryBuffer>> listBuffer = MemoryBuffer::getFile(InputPath); if (std::error_code EC = listBuffer.getError()) return EC; // Parse the header list into strings. SmallVector<StringRef, 32> Strings; listBuffer.get()->getBuffer().split(Strings, "\n", -1, false); // Collect the header file names from the string list. for (SmallVectorImpl<StringRef>::iterator I = Strings.begin(), E = Strings.end(); I != E; ++I) { StringRef Line = I->trim(); // Ignore comments and empty lines. if (Line.empty() || (Line[0] == '#')) continue; std::pair<StringRef, StringRef> TargetAndDependents = Line.split(':'); SmallString<256> HeaderFileName; // Prepend header file name prefix if it's not absolute. if (llvm::sys::path::is_absolute(TargetAndDependents.first)) llvm::sys::path::native(TargetAndDependents.first, HeaderFileName); else { if (HeaderDirectory.size() != 0) HeaderFileName = HeaderDirectory; else HeaderFileName = CurrentDirectory; llvm::sys::path::append(HeaderFileName, TargetAndDependents.first); llvm::sys::path::native(HeaderFileName); } // Handle optional dependencies. DependentsVector Dependents; SmallVector<StringRef, 4> DependentsList; TargetAndDependents.second.split(DependentsList, " ", -1, false); int Count = DependentsList.size(); for (int Index = 0; Index < Count; ++Index) { SmallString<256> Dependent; if (llvm::sys::path::is_absolute(DependentsList[Index])) Dependent = DependentsList[Index]; else { if (HeaderDirectory.size() != 0) Dependent = HeaderDirectory; else Dependent = CurrentDirectory; llvm::sys::path::append(Dependent, DependentsList[Index]); } llvm::sys::path::native(Dependent); Dependents.push_back(getCanonicalPath(Dependent.str())); } // Get canonical form. HeaderFileName = getCanonicalPath(HeaderFileName); // Save the resulting header file path and dependencies. HeaderFileNames.push_back(HeaderFileName.str()); Dependencies[HeaderFileName.str()] = Dependents; } return std::error_code(); }
static StringRef StripTrailingDots(StringRef s) { for (StringRef::size_type i = s.size(); i != 0; --i) if (s[i - 1] != '.') return s.substr(0, i); return ""; }
/// GetDwarfFile - takes a file name an number to place in the dwarf file and /// directory tables. If the file number has already been allocated it is an /// error and zero is returned and the client reports the error, else the /// allocated file number is returned. The file numbers may be in any order. unsigned MCContext::GetDwarfFile(StringRef Directory, StringRef FileName, unsigned FileNumber, unsigned CUID) { // TODO: a FileNumber of zero says to use the next available file number. // Note: in GenericAsmParser::ParseDirectiveFile() FileNumber was checked // to not be less than one. This needs to be change to be not less than zero. SmallVectorImpl<MCDwarfFile *>& MCDwarfFiles = MCDwarfFilesCUMap[CUID]; SmallVectorImpl<StringRef>& MCDwarfDirs = MCDwarfDirsCUMap[CUID]; // Make space for this FileNumber in the MCDwarfFiles vector if needed. if (FileNumber >= MCDwarfFiles.size()) { MCDwarfFiles.resize(FileNumber + 1); } else { MCDwarfFile *&ExistingFile = MCDwarfFiles[FileNumber]; if (ExistingFile) // It is an error to use see the same number more than once. return 0; } // Get the new MCDwarfFile slot for this FileNumber. MCDwarfFile *&File = MCDwarfFiles[FileNumber]; if (Directory.empty()) { // Separate the directory part from the basename of the FileName. StringRef tFileName = sys::path::filename(FileName); if (!tFileName.empty()) { Directory = sys::path::parent_path(FileName); if (!Directory.empty()) FileName = tFileName; } } // Find or make a entry in the MCDwarfDirs vector for this Directory. // Capture directory name. unsigned DirIndex; if (Directory.empty()) { // For FileNames with no directories a DirIndex of 0 is used. DirIndex = 0; } else { DirIndex = 0; for (unsigned End = MCDwarfDirs.size(); DirIndex < End; DirIndex++) { if (Directory == MCDwarfDirs[DirIndex]) break; } if (DirIndex >= MCDwarfDirs.size()) { char *Buf = static_cast<char *>(Allocate(Directory.size())); memcpy(Buf, Directory.data(), Directory.size()); MCDwarfDirs.push_back(StringRef(Buf, Directory.size())); } // The DirIndex is one based, as DirIndex of 0 is used for FileNames with // no directories. MCDwarfDirs[] is unlike MCDwarfFiles[] in that the // directory names are stored at MCDwarfDirs[DirIndex-1] where FileNames // are stored at MCDwarfFiles[FileNumber].Name . DirIndex++; } // Now make the MCDwarfFile entry and place it in the slot in the MCDwarfFiles // vector. char *Buf = static_cast<char *>(Allocate(FileName.size())); memcpy(Buf, FileName.data(), FileName.size()); File = new (*this) MCDwarfFile(StringRef(Buf, FileName.size()), DirIndex); // return the allocated FileNumber. return FileNumber; }
/// Interpreting the given string using the normal CamelCase /// conventions, determine whether the given string starts with the /// given "word", which is assumed to end in a lowercase letter. static bool startsWithWord(StringRef name, StringRef word) { if (name.size() < word.size()) return false; return ((name.size() == word.size() || !isLowercase(name[word.size()])) && name.startswith(word)); }
/// emitModuleFlags - Perform code emission for module flags. void TargetLoweringObjectFileMachO::emitModuleFlags( MCStreamer &Streamer, ArrayRef<Module::ModuleFlagEntry> ModuleFlags, const TargetMachine &TM) const { unsigned VersionVal = 0; unsigned ImageInfoFlags = 0; MDNode *LinkerOptions = nullptr; StringRef SectionVal; for (const auto &MFE : ModuleFlags) { // Ignore flags with 'Require' behavior. if (MFE.Behavior == Module::Require) continue; StringRef Key = MFE.Key->getString(); Metadata *Val = MFE.Val; if (Key == "Objective-C Image Info Version") { VersionVal = mdconst::extract<ConstantInt>(Val)->getZExtValue(); } else if (Key == "Objective-C Garbage Collection" || Key == "Objective-C GC Only" || Key == "Objective-C Is Simulated" || Key == "Objective-C Class Properties" || Key == "Objective-C Image Swift Version") { ImageInfoFlags |= mdconst::extract<ConstantInt>(Val)->getZExtValue(); } else if (Key == "Objective-C Image Info Section") { SectionVal = cast<MDString>(Val)->getString(); } else if (Key == "Linker Options") { LinkerOptions = cast<MDNode>(Val); } } // Emit the linker options if present. if (LinkerOptions) { for (const auto &Option : LinkerOptions->operands()) { SmallVector<std::string, 4> StrOptions; for (const auto &Piece : cast<MDNode>(Option)->operands()) StrOptions.push_back(cast<MDString>(Piece)->getString()); Streamer.EmitLinkerOptions(StrOptions); } } // The section is mandatory. If we don't have it, then we don't have GC info. if (SectionVal.empty()) return; StringRef Segment, Section; unsigned TAA = 0, StubSize = 0; bool TAAParsed; std::string ErrorCode = MCSectionMachO::ParseSectionSpecifier(SectionVal, Segment, Section, TAA, TAAParsed, StubSize); if (!ErrorCode.empty()) // If invalid, report the error with report_fatal_error. report_fatal_error("Invalid section specifier '" + Section + "': " + ErrorCode + "."); // Get the section. MCSectionMachO *S = getContext().getMachOSection( Segment, Section, TAA, StubSize, SectionKind::getData()); Streamer.SwitchSection(S); Streamer.EmitLabel(getContext(). getOrCreateSymbol(StringRef("L_OBJC_IMAGE_INFO"))); Streamer.EmitIntValue(VersionVal, 4); Streamer.EmitIntValue(ImageInfoFlags, 4); Streamer.AddBlankLine(); }
static SectionKind getELFKindForNamedSection(StringRef Name, SectionKind K) { // N.B.: The defaults used in here are no the same ones used in MC. // We follow gcc, MC follows gas. For example, given ".section .eh_frame", // both gas and MC will produce a section with no flags. Given // section(".eh_frame") gcc will produce: // // .section .eh_frame,"a",@progbits if (Name == getInstrProfCoverageSectionName(false)) return SectionKind::getMetadata(); if (Name.empty() || Name[0] != '.') return K; // Some lame default implementation based on some magic section names. if (Name == ".bss" || Name.startswith(".bss.") || Name.startswith(".gnu.linkonce.b.") || Name.startswith(".llvm.linkonce.b.") || Name == ".sbss" || Name.startswith(".sbss.") || Name.startswith(".gnu.linkonce.sb.") || Name.startswith(".llvm.linkonce.sb.")) return SectionKind::getBSS(); if (Name == ".tdata" || Name.startswith(".tdata.") || Name.startswith(".gnu.linkonce.td.") || Name.startswith(".llvm.linkonce.td.")) return SectionKind::getThreadData(); if (Name == ".tbss" || Name.startswith(".tbss.") || Name.startswith(".gnu.linkonce.tb.") || Name.startswith(".llvm.linkonce.tb.")) return SectionKind::getThreadBSS(); return K; }
/// Parse - Analyze the specified string (e.g. "==&{eax}") and fill in the /// fields in this structure. If the constraint string is not understood, /// return true, otherwise return false. bool InlineAsm::ConstraintInfo::Parse(StringRef Str, InlineAsm::ConstraintInfoVector &ConstraintsSoFar) { StringRef::iterator I = Str.begin(), E = Str.end(); unsigned multipleAlternativeCount = Str.count('|') + 1; unsigned multipleAlternativeIndex = 0; ConstraintCodeVector *pCodes = &Codes; // Initialize isMultipleAlternative = (multipleAlternativeCount > 1 ? true : false); if (isMultipleAlternative) { multipleAlternatives.resize(multipleAlternativeCount); pCodes = &multipleAlternatives[0].Codes; } Type = isInput; isEarlyClobber = false; MatchingInput = -1; isCommutative = false; isIndirect = false; currentAlternativeIndex = 0; // Parse prefixes. if (*I == '~') { Type = isClobber; ++I; } else if (*I == '=') { ++I; Type = isOutput; } if (*I == '*') { isIndirect = true; ++I; } if (I == E) return true; // Just a prefix, like "==" or "~". // Parse the modifiers. bool DoneWithModifiers = false; while (!DoneWithModifiers) { switch (*I) { default: DoneWithModifiers = true; break; case '&': // Early clobber. if (Type != isOutput || // Cannot early clobber anything but output. isEarlyClobber) // Reject &&&&&& return true; isEarlyClobber = true; break; case '%': // Commutative. if (Type == isClobber || // Cannot commute clobbers. isCommutative) // Reject %%%%% return true; isCommutative = true; break; case '#': // Comment. case '*': // Register preferencing. return true; // Not supported. } if (!DoneWithModifiers) { ++I; if (I == E) return true; // Just prefixes and modifiers! } } // Parse the various constraints. while (I != E) { if (*I == '{') { // Physical register reference. // Find the end of the register name. StringRef::iterator ConstraintEnd = std::find(I+1, E, '}'); if (ConstraintEnd == E) return true; // "{foo" pCodes->push_back(std::string(I, ConstraintEnd+1)); I = ConstraintEnd+1; } else if (isdigit(static_cast<unsigned char>(*I))) { // Matching Constraint // Maximal munch numbers. StringRef::iterator NumStart = I; while (I != E && isdigit(static_cast<unsigned char>(*I))) ++I; pCodes->push_back(std::string(NumStart, I)); unsigned N = atoi(pCodes->back().c_str()); // Check that this is a valid matching constraint! if (N >= ConstraintsSoFar.size() || ConstraintsSoFar[N].Type != isOutput|| Type != isInput) return true; // Invalid constraint number. // If Operand N already has a matching input, reject this. An output // can't be constrained to the same value as multiple inputs. if (isMultipleAlternative) { InlineAsm::SubConstraintInfo &scInfo = ConstraintsSoFar[N].multipleAlternatives[multipleAlternativeIndex]; if (scInfo.MatchingInput != -1) return true; // Note that operand #n has a matching input. scInfo.MatchingInput = ConstraintsSoFar.size(); } else { if (ConstraintsSoFar[N].hasMatchingInput()) return true; // Note that operand #n has a matching input. ConstraintsSoFar[N].MatchingInput = ConstraintsSoFar.size(); } } else if (*I == '|') { multipleAlternativeIndex++; pCodes = &multipleAlternatives[multipleAlternativeIndex].Codes; ++I; } else if (*I == '^') { // Multi-letter constraint // FIXME: For now assuming these are 2-character constraints. pCodes->push_back(std::string(I+1, I+3)); I += 3; } else { // Single letter constraint. pCodes->push_back(std::string(I, I+1)); ++I; } } return false; }
/// Parse \p Input as line sample. /// /// \param Input input line. /// \param IsCallsite true if the line represents an inlined callsite. /// \param Depth the depth of the inline stack. /// \param NumSamples total samples of the line/inlined callsite. /// \param LineOffset line offset to the start of the function. /// \param Discriminator discriminator of the line. /// \param TargetCountMap map from indirect call target to count. /// /// returns true if parsing is successful. static bool ParseLine(const StringRef &Input, bool &IsCallsite, uint32_t &Depth, uint64_t &NumSamples, uint32_t &LineOffset, uint32_t &Discriminator, StringRef &CalleeName, DenseMap<StringRef, uint64_t> &TargetCountMap) { for (Depth = 0; Input[Depth] == ' '; Depth++) ; if (Depth == 0) return false; size_t n1 = Input.find(':'); StringRef Loc = Input.substr(Depth, n1 - Depth); size_t n2 = Loc.find('.'); if (n2 == StringRef::npos) { if (Loc.getAsInteger(10, LineOffset) || !isOffsetLegal(LineOffset)) return false; Discriminator = 0; } else { if (Loc.substr(0, n2).getAsInteger(10, LineOffset)) return false; if (Loc.substr(n2 + 1).getAsInteger(10, Discriminator)) return false; } StringRef Rest = Input.substr(n1 + 2); if (Rest[0] >= '0' && Rest[0] <= '9') { IsCallsite = false; size_t n3 = Rest.find(' '); if (n3 == StringRef::npos) { if (Rest.getAsInteger(10, NumSamples)) return false; } else { if (Rest.substr(0, n3).getAsInteger(10, NumSamples)) return false; } // Find call targets and their sample counts. // Note: In some cases, there are symbols in the profile which are not // mangled. To accommodate such cases, use colon + integer pairs as the // anchor points. // An example: // _M_construct<char *>:1000 string_view<std::allocator<char> >:437 // ":1000" and ":437" are used as anchor points so the string above will // be interpreted as // target: _M_construct<char *> // count: 1000 // target: string_view<std::allocator<char> > // count: 437 while (n3 != StringRef::npos) { n3 += Rest.substr(n3).find_first_not_of(' '); Rest = Rest.substr(n3); n3 = Rest.find_first_of(':'); if (n3 == StringRef::npos || n3 == 0) return false; StringRef Target; uint64_t count, n4; while (true) { // Get the segment after the current colon. StringRef AfterColon = Rest.substr(n3 + 1); // Get the target symbol before the current colon. Target = Rest.substr(0, n3); // Check if the word after the current colon is an integer. n4 = AfterColon.find_first_of(' '); n4 = (n4 != StringRef::npos) ? n3 + n4 + 1 : Rest.size(); StringRef WordAfterColon = Rest.substr(n3 + 1, n4 - n3 - 1); if (!WordAfterColon.getAsInteger(10, count)) break; // Try to find the next colon. uint64_t n5 = AfterColon.find_first_of(':'); if (n5 == StringRef::npos) return false; n3 += n5 + 1; } // An anchor point is found. Save the {target, count} pair TargetCountMap[Target] = count; if (n4 == Rest.size()) break; // Change n3 to the next blank space after colon + integer pair. n3 = n4; } } else { IsCallsite = true; size_t n3 = Rest.find_last_of(':'); CalleeName = Rest.substr(0, n3); if (Rest.substr(n3 + 1).getAsInteger(10, NumSamples)) return false; } return true; }
/// \brief Format a single diagnostic argument and write it to the given /// stream. static void formatDiagnosticArgument(StringRef Modifier, StringRef ModifierArguments, ArrayRef<DiagnosticArgument> Args, unsigned ArgIndex, llvm::raw_ostream &Out) { const DiagnosticArgument &Arg = Args[ArgIndex]; switch (Arg.getKind()) { case DiagnosticArgumentKind::Integer: if (Modifier == "select") { assert(Arg.getAsInteger() >= 0 && "Negative selection index"); formatSelectionArgument(ModifierArguments, Args, Arg.getAsInteger(), Out); } else if (Modifier == "s") { if (Arg.getAsInteger() != 1) Out << 's'; } else { assert(Modifier.empty() && "Improper modifier for integer argument"); Out << Arg.getAsInteger(); } break; case DiagnosticArgumentKind::Unsigned: if (Modifier == "select") { formatSelectionArgument(ModifierArguments, Args, Arg.getAsUnsigned(), Out); } else if (Modifier == "s") { if (Arg.getAsUnsigned() != 1) Out << 's'; } else { assert(Modifier.empty() && "Improper modifier for unsigned argument"); Out << Arg.getAsUnsigned(); } break; case DiagnosticArgumentKind::String: assert(Modifier.empty() && "Improper modifier for string argument"); Out << Arg.getAsString(); break; case DiagnosticArgumentKind::Identifier: assert(Modifier.empty() && "Improper modifier for identifier argument"); Out << '\''; Arg.getAsIdentifier().printPretty(Out); Out << '\''; break; case DiagnosticArgumentKind::ObjCSelector: assert(Modifier.empty() && "Improper modifier for selector argument"); Out << '\'' << Arg.getAsObjCSelector() << '\''; break; case DiagnosticArgumentKind::Type: { assert(Modifier.empty() && "Improper modifier for Type argument"); // Strip extraneous parentheses; they add no value. auto type = Arg.getAsType()->getWithoutParens(); std::string typeName = type->getString(); Out << '\'' << typeName << '\''; // Decide whether to show the desugared type or not. We filter out some // cases to avoid too much noise. bool showAKA = !type->isCanonical(); // Substituted types are uninteresting sugar that prevents the heuristics // below from kicking in. if (showAKA) if (auto *ST = dyn_cast<SubstitutedType>(type.getPointer())) type = ST->getReplacementType(); // If we're complaining about a function type, don't "aka" just because of // differences in the argument or result types. if (showAKA && type->is<FunctionType>() && isa<FunctionType>(type.getPointer())) showAKA = false; // Don't unwrap intentional sugar types like T? or [T]. if (showAKA && (isa<SyntaxSugarType>(type.getPointer()) || isa<DictionaryType>(type.getPointer()) || type->is<BuiltinType>())) showAKA = false; // If they are textually the same, don't show them. This can happen when // they are actually different types, because they exist in different scopes // (e.g. everyone names their type parameters 'T'). if (showAKA && typeName == type->getCanonicalType()->getString()) showAKA = false; // Don't show generic type parameters. if (showAKA && type->getCanonicalType()->hasTypeParameter()) showAKA = false; if (showAKA) Out << " (aka '" << type->getCanonicalType() << "')"; break; } case DiagnosticArgumentKind::TypeRepr: assert(Modifier.empty() && "Improper modifier for TypeRepr argument"); Out << '\'' << Arg.getAsTypeRepr() << '\''; break; case DiagnosticArgumentKind::PatternKind: assert(Modifier.empty() && "Improper modifier for PatternKind argument"); Out << Arg.getAsPatternKind(); break; case DiagnosticArgumentKind::StaticSpellingKind: if (Modifier == "select") { formatSelectionArgument(ModifierArguments, Args, unsigned(Arg.getAsStaticSpellingKind()), Out); } else { assert(Modifier.empty() && "Improper modifier for StaticSpellingKind argument"); Out << Arg.getAsStaticSpellingKind(); } break; case DiagnosticArgumentKind::DescriptiveDeclKind: assert(Modifier.empty() && "Improper modifier for DescriptiveDeclKind argument"); Out << Decl::getDescriptiveKindName(Arg.getAsDescriptiveDeclKind()); break; case DiagnosticArgumentKind::DeclAttribute: assert(Modifier.empty() && "Improper modifier for DeclAttribute argument"); if (Arg.getAsDeclAttribute()->isDeclModifier()) Out << '\'' << Arg.getAsDeclAttribute()->getAttrName() << '\''; else Out << '@' << Arg.getAsDeclAttribute()->getAttrName(); break; case DiagnosticArgumentKind::VersionTuple: assert(Modifier.empty() && "Improper modifier for VersionTuple argument"); Out << Arg.getAsVersionTuple().getAsString(); break; } }
/// Returns the common word-prefix of two strings, allowing the second string /// to be a common English plural form of the first. /// /// For example, given "NSProperty" and "NSProperties", the full "NSProperty" /// is returned. Given "NSMagicArmor" and "NSMagicArmory", only /// "NSMagic" is returned. /// /// The "-s", "-es", and "-ies" patterns cover every plural NS_OPTIONS name /// in Cocoa and Cocoa Touch. /// /// \see getCommonWordPrefix static StringRef getCommonPluralPrefix(StringRef singular, StringRef plural) { assert(!plural.empty()); if (singular.empty()) return singular; bool ignored; StringRef commonPrefix = getCommonWordPrefix(singular, plural, ignored); if (commonPrefix.size() == singular.size() || plural.back() != 's') return commonPrefix; StringRef leftover = singular.substr(commonPrefix.size()); StringRef firstLeftoverWord = camel_case::getFirstWord(leftover); StringRef commonPrefixPlusWord = singular.substr(0, commonPrefix.size() + firstLeftoverWord.size()); // Is the plural string just "[singular]s"? plural = plural.drop_back(); if (plural.endswith(firstLeftoverWord)) return commonPrefixPlusWord; if (plural.empty() || plural.back() != 'e') return commonPrefix; // Is the plural string "[singular]es"? plural = plural.drop_back(); if (plural.endswith(firstLeftoverWord)) return commonPrefixPlusWord; if (plural.empty() || !(plural.back() == 'i' && singular.back() == 'y')) return commonPrefix; // Is the plural string "[prefix]ies" and the singular "[prefix]y"? plural = plural.drop_back(); firstLeftoverWord = firstLeftoverWord.drop_back(); if (plural.endswith(firstLeftoverWord)) return commonPrefixPlusWord; return commonPrefix; }
/// \brief Format the given diagnostic text and place the result in the given /// buffer. static void formatDiagnosticText(StringRef InText, ArrayRef<DiagnosticArgument> Args, llvm::raw_ostream &Out) { while (!InText.empty()) { size_t Percent = InText.find('%'); if (Percent == StringRef::npos) { // Write the rest of the string; we're done. Out.write(InText.data(), InText.size()); break; } // Write the string up to (but not including) the %, then drop that text // (including the %). Out.write(InText.data(), Percent); InText = InText.substr(Percent + 1); // '%%' -> '%'. if (InText[0] == '%') { Out.write('%'); InText = InText.substr(1); continue; } // Parse an optional modifier. StringRef Modifier; { unsigned Length = 0; while (isalpha(InText[Length])) ++Length; Modifier = InText.substr(0, Length); InText = InText.substr(Length); } // Parse the optional argument list for a modifier, which is brace-enclosed. StringRef ModifierArguments; if (InText[0] == '{') { InText = InText.substr(1); ModifierArguments = skipToDelimiter(InText, '}'); } // Find the digit sequence. unsigned Length = 0; for (size_t N = InText.size(); Length != N; ++Length) { if (!isdigit(InText[Length])) break; } // Parse the digit sequence into an argument index. unsigned ArgIndex; bool Result = InText.substr(0, Length).getAsInteger(10, ArgIndex); assert(!Result && "Unparseable argument index value?"); (void)Result; assert(ArgIndex < Args.size() && "Out-of-range argument index"); InText = InText.substr(Length); // Convert the argument to a string. formatDiagnosticArgument(Modifier, ModifierArguments, Args, ArgIndex, Out); } }
/// Mangle this entity into the given stream. std::string LinkEntity::mangleNew() const { // Almost everything below gets the common prefix: // mangled-name ::= '_T' global IRGenMangler mangler; switch (getKind()) { // global ::= 'w' value-witness-kind // value witness case Kind::ValueWitness: return mangler.mangleValueWitness(getType(), getValueWitness()); // global ::= 'WV' type // value witness case Kind::ValueWitnessTable: return mangler.mangleValueWitnessTable(getType()); // global ::= 't' type // Abstract type manglings just follow <type>. case Kind::TypeMangling: return mangleOld(); // global ::= 'Ma' type // type metadata access function case Kind::TypeMetadataAccessFunction: return mangler.mangleTypeMetadataAccessFunction(getType()); // global ::= 'ML' type // type metadata lazy cache variable case Kind::TypeMetadataLazyCacheVariable: return mangler.mangleTypeMetadataLazyCacheVariable(getType()); // global ::= 'Mf' type // 'full' type metadata // global ::= 'M' directness type // type metadata // global ::= 'MP' directness type // type metadata pattern case Kind::TypeMetadata: switch (getMetadataAddress()) { case TypeMetadataAddress::FullMetadata: return mangler.mangleTypeFullMetadataFull(getType()); case TypeMetadataAddress::AddressPoint: return mangler.mangleTypeMetadataFull(getType(), isMetadataPattern()); } llvm_unreachable("invalid metadata address"); // global ::= 'M' directness type // type metadata case Kind::ForeignTypeMetadataCandidate: return mangler.mangleTypeMetadataFull(getType(), /*isPattern=*/false); // global ::= 'Mm' type // class metaclass case Kind::SwiftMetaclassStub: return mangler.mangleClassMetaClass(cast<ClassDecl>(getDecl())); // global ::= 'Mn' type // nominal type descriptor case Kind::NominalTypeDescriptor: return mangler.mangleNominalTypeDescriptor( cast<NominalTypeDecl>(getDecl())); // global ::= 'Mp' type // protocol descriptor case Kind::ProtocolDescriptor: return mangler.mangleProtocolDescriptor(cast<ProtocolDecl>(getDecl())); // global ::= 'Wo' entity case Kind::WitnessTableOffset: return mangler.mangleWitnessTableOffset(getDecl()); // global ::= 'Wv' directness entity case Kind::FieldOffset: return mangler.mangleFieldOffsetFull(getDecl(), isOffsetIndirect()); // global ::= 'WP' protocol-conformance case Kind::DirectProtocolWitnessTable: return mangler.mangleDirectProtocolWitnessTable(getProtocolConformance()); // global ::= 'WG' protocol-conformance case Kind::GenericProtocolWitnessTableCache: return mangler.mangleGenericProtocolWitnessTableCache( getProtocolConformance()); // global ::= 'WI' protocol-conformance case Kind::GenericProtocolWitnessTableInstantiationFunction: return mangler.mangleGenericProtocolWitnessTableInstantiationFunction( getProtocolConformance()); // global ::= 'Wa' protocol-conformance case Kind::ProtocolWitnessTableAccessFunction: return mangler.mangleProtocolWitnessTableAccessFunction( getProtocolConformance()); // global ::= 'Wl' type protocol-conformance case Kind::ProtocolWitnessTableLazyAccessFunction: return mangler.mangleProtocolWitnessTableLazyAccessFunction(getType(), getProtocolConformance()); // global ::= 'WL' type protocol-conformance case Kind::ProtocolWitnessTableLazyCacheVariable: return mangler.mangleProtocolWitnessTableLazyCacheVariable(getType(), getProtocolConformance()); // global ::= 'Wt' protocol-conformance identifier case Kind::AssociatedTypeMetadataAccessFunction: return mangler.mangleAssociatedTypeMetadataAccessFunction( getProtocolConformance(), getAssociatedType()->getNameStr()); // global ::= 'WT' protocol-conformance identifier nominal-type case Kind::AssociatedTypeWitnessTableAccessFunction: return mangler.mangleAssociatedTypeWitnessTableAccessFunction( getProtocolConformance(), getAssociatedType()->getNameStr(), getAssociatedProtocol()); // For all the following, this rule was imposed above: // global ::= local-marker? entity // some identifiable thing // entity ::= declaration // other declaration case Kind::Function: // As a special case, functions can have manually mangled names. if (auto AsmA = getDecl()->getAttrs().getAttribute<SILGenNameAttr>()) { return AsmA->Name; } // Otherwise, fall through into the 'other decl' case. SWIFT_FALLTHROUGH; case Kind::Other: // As a special case, Clang functions and globals don't get mangled at all. if (auto clangDecl = getDecl()->getClangDecl()) { if (auto namedClangDecl = dyn_cast<clang::DeclaratorDecl>(clangDecl)) { if (auto asmLabel = namedClangDecl->getAttr<clang::AsmLabelAttr>()) { std::string Name(1, '\01'); Name.append(asmLabel->getLabel()); return Name; } if (namedClangDecl->hasAttr<clang::OverloadableAttr>()) { // FIXME: When we can import C++, use Clang's mangler all the time. std::string storage; llvm::raw_string_ostream SS(storage); mangleClangDecl(SS, namedClangDecl, getDecl()->getASTContext()); return SS.str(); } return namedClangDecl->getName(); } } if (auto type = dyn_cast<NominalTypeDecl>(getDecl())) { return mangler.mangleNominalType(type); } if (auto ctor = dyn_cast<ConstructorDecl>(getDecl())) { // FIXME: Hack. LinkInfo should be able to refer to the allocating // constructor rather than inferring it here. return mangler.mangleConstructorEntity(ctor, /*isAllocating=*/true, /*isCurried=*/false); } return mangler.mangleEntity(getDecl(), /*isCurried=*/false); // An Objective-C class reference reference. The symbol is private, so // the mangling is unimportant; it should just be readable in LLVM IR. case Kind::ObjCClassRef: { llvm::SmallString<64> tempBuffer; StringRef name = cast<ClassDecl>(getDecl())->getObjCRuntimeName(tempBuffer); std::string Result("OBJC_CLASS_REF_$_"); Result.append(name.data(), name.size()); return Result; } // An Objective-C class reference; not a swift mangling. case Kind::ObjCClass: { llvm::SmallString<64> TempBuffer; StringRef Name = cast<ClassDecl>(getDecl())->getObjCRuntimeName(TempBuffer); std::string Result("OBJC_CLASS_$_"); Result.append(Name.data(), Name.size()); return Result; } // An Objective-C metaclass reference; not a swift mangling. case Kind::ObjCMetaclass: { llvm::SmallString<64> TempBuffer; StringRef Name = cast<ClassDecl>(getDecl())->getObjCRuntimeName(TempBuffer); std::string Result("OBJC_METACLASS_$_"); Result.append(Name.data(), Name.size()); return Result; } case Kind::SILFunction: return getSILFunction()->getName(); case Kind::SILGlobalVariable: return getSILGlobalVariable()->getName(); case Kind::ReflectionBuiltinDescriptor: return mangler.mangleReflectionBuiltinDescriptor(getType()); case Kind::ReflectionFieldDescriptor: return mangler.mangleReflectionFieldDescriptor(getType()); case Kind::ReflectionAssociatedTypeDescriptor: return mangler.mangleReflectionAssociatedTypeDescriptor( getProtocolConformance()); case Kind::ReflectionSuperclassDescriptor: return mangler.mangleReflectionSuperclassDescriptor( cast<ClassDecl>(getDecl())); } llvm_unreachable("bad entity kind!"); }
void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, const PathDiagnosticPiece& P, unsigned num, unsigned max) { // For now, just draw a box above the line in question, and emit the // warning. FullSourceLoc Pos = P.getLocation().asLocation(); if (!Pos.isValid()) return; SourceManager &SM = R.getSourceMgr(); assert(&Pos.getManager() == &SM && "SourceManagers are different!"); std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedExpansionLoc(Pos); if (LPosInfo.first != BugFileID) return; const llvm::MemoryBuffer *Buf = SM.getBuffer(LPosInfo.first); const char* FileStart = Buf->getBufferStart(); // Compute the column number. Rewind from the current position to the start // of the line. unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second); const char *TokInstantiationPtr =Pos.getExpansionLoc().getCharacterData(); const char *LineStart = TokInstantiationPtr-ColNo; // Compute LineEnd. const char *LineEnd = TokInstantiationPtr; const char* FileEnd = Buf->getBufferEnd(); while (*LineEnd != '\n' && LineEnd != FileEnd) ++LineEnd; // Compute the margin offset by counting tabs and non-tabs. unsigned PosNo = 0; for (const char* c = LineStart; c != TokInstantiationPtr; ++c) PosNo += *c == '\t' ? 8 : 1; // Create the html for the message. const char *Kind = 0; switch (P.getKind()) { case PathDiagnosticPiece::Call: llvm_unreachable("Calls should already be handled"); case PathDiagnosticPiece::Event: Kind = "Event"; break; case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break; // Setting Kind to "Control" is intentional. case PathDiagnosticPiece::Macro: Kind = "Control"; break; } std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\""; if (num == max) os << "EndPath"; else os << "Path" << num; os << "\" class=\"msg"; if (Kind) os << " msg" << Kind; os << "\" style=\"margin-left:" << PosNo << "ex"; // Output a maximum size. if (!isa<PathDiagnosticMacroPiece>(P)) { // Get the string and determining its maximum substring. const std::string& Msg = P.getString(); unsigned max_token = 0; unsigned cnt = 0; unsigned len = Msg.size(); for (std::string::const_iterator I=Msg.begin(), E=Msg.end(); I!=E; ++I) switch (*I) { default: ++cnt; continue; case ' ': case '\t': case '\n': if (cnt > max_token) max_token = cnt; cnt = 0; } if (cnt > max_token) max_token = cnt; // Determine the approximate size of the message bubble in em. unsigned em; const unsigned max_line = 120; if (max_token >= max_line) em = max_token / 2; else { unsigned characters = max_line; unsigned lines = len / max_line; if (lines > 0) { for (; characters > max_token; --characters) if (len / characters > lines) { ++characters; break; } } em = characters / 2; } if (em < max_line/2) os << "; max-width:" << em << "em"; } else os << "; max-width:100em"; os << "\">"; if (max > 1) { os << "<table class=\"msgT\"><tr><td valign=\"top\">"; os << "<div class=\"PathIndex"; if (Kind) os << " PathIndex" << Kind; os << "\">" << num << "</div>"; if (num > 1) { os << "</td><td><div class=\"PathNav\"><a href=\"#Path" << (num - 1) << "\" title=\"Previous event (" << (num - 1) << ")\">←</a></div></td>"; } os << "</td><td>"; } if (const PathDiagnosticMacroPiece *MP = dyn_cast<PathDiagnosticMacroPiece>(&P)) { os << "Within the expansion of the macro '"; // Get the name of the macro by relexing it. { FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc(); assert(L.isFileID()); StringRef BufferInfo = L.getBufferData(); std::pair<FileID, unsigned> LocInfo = L.getDecomposedLoc(); const char* MacroName = LocInfo.second + BufferInfo.data(); Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(), BufferInfo.begin(), MacroName, BufferInfo.end()); Token TheTok; rawLexer.LexFromRawLexer(TheTok); for (unsigned i = 0, n = TheTok.getLength(); i < n; ++i) os << MacroName[i]; } os << "':\n"; if (max > 1) { os << "</td>"; if (num < max) { os << "<td><div class=\"PathNav\"><a href=\"#"; if (num == max - 1) os << "EndPath"; else os << "Path" << (num + 1); os << "\" title=\"Next event (" << (num + 1) << ")\">→</a></div></td>"; } os << "</tr></table>"; } // Within a macro piece. Write out each event. ProcessMacroPiece(os, *MP, 0); } else { os << html::EscapeText(P.getString()); if (max > 1) { os << "</td>"; if (num < max) { os << "<td><div class=\"PathNav\"><a href=\"#"; if (num == max - 1) os << "EndPath"; else os << "Path" << (num + 1); os << "\" title=\"Next event (" << (num + 1) << ")\">→</a></div></td>"; } os << "</tr></table>"; } } os << "</div></td></tr>"; // Insert the new html. unsigned DisplayPos = LineEnd - FileStart; SourceLocation Loc = SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos); R.InsertTextBefore(Loc, os.str()); // Now highlight the ranges. ArrayRef<SourceRange> Ranges = P.getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { HighlightRange(R, LPosInfo.first, *I); } }
ErrorOr<StringRef> Archive::Child::getName() const { StringRef name = getRawName(); // Check if it's a special name. if (name[0] == '/') { if (name.size() == 1) // Linker member. return name; if (name.size() == 2 && name[1] == '/') // String table. return name; // It's a long name. // Get the offset. std::size_t offset; if (name.substr(1).rtrim(' ').getAsInteger(10, offset)) llvm_unreachable("Long name offset is not an integer"); // Verify it. if (offset >= Parent->StringTable.size()) return object_error::parse_failed; const char *addr = Parent->StringTable.begin() + offset; // GNU long file names end with a "/\n". if (Parent->kind() == K_GNU || Parent->kind() == K_MIPS64) { StringRef::size_type End = StringRef(addr).find('\n'); return StringRef(addr, End - 1); } return StringRef(addr); } else if (name.startswith("#1/")) { uint64_t name_size; if (name.substr(3).rtrim(' ').getAsInteger(10, name_size)) llvm_unreachable("Long name length is not an ingeter"); return Data.substr(Header.getSizeOf(), name_size).rtrim('\0'); } else { // It is not a long name so trim the blanks at the end of the name. if (name[name.size() - 1] != '/') { return name.rtrim(' '); } } // It's a simple name. if (name[name.size() - 1] == '/') return name.substr(0, name.size() - 1); return name; }
// IsTransformableFunction - Return true if the function name isn't one // of the ones we don't want transformed. Currently, don't transform any // "llvm.{setjmp,longjmp}" functions and none of the setjmp/longjmp error // handling functions (beginning with __llvm_sjljeh_...they don't throw // exceptions). bool LowerSetJmp::IsTransformableFunction(StringRef Name) { return !Name.startswith("__llvm_sjljeh_"); }
void PlistDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) { // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; SmallVector<FileID, 10> Fids; const SourceManager* SM = 0; if (!Diags.empty()) SM = &(*(*Diags.begin())->path.begin())->getLocation().getManager(); for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), DE = Diags.end(); DI != DE; ++DI) { const PathDiagnostic *D = *DI; SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { const PathPieces &path = *WorkList.pop_back_val(); for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I) { const PathDiagnosticPiece *piece = I->getPtr(); AddFID(FM, Fids, *SM, piece->getLocation().asLocation()); ArrayRef<SourceRange> Ranges = piece->getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { AddFID(FM, Fids, *SM, I->getBegin()); AddFID(FM, Fids, *SM, I->getEnd()); } if (const PathDiagnosticCallPiece *call = dyn_cast<PathDiagnosticCallPiece>(piece)) { IntrusiveRefCntPtr<PathDiagnosticEventPiece> callEnterWithin = call->getCallEnterWithinCallerEvent(); if (callEnterWithin) AddFID(FM, Fids, *SM, callEnterWithin->getLocation().asLocation()); WorkList.push_back(&call->path); } else if (const PathDiagnosticMacroPiece *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) { WorkList.push_back(¯o->subPieces); } } } } // Open the file. std::string ErrMsg; llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg, llvm::sys::fs::F_Text); if (!ErrMsg.empty()) { llvm::errs() << "warning: could not create file: " << OutputFile << '\n'; return; } // Write the plist header. o << PlistHeader; // Write the root object: a <dict> containing... // - "clang_version", the string representation of clang version // - "files", an <array> mapping from FIDs to file names // - "diagnostics", an <array> containing the path diagnostics o << "<dict>\n" << " <key>clang_version</key>\n"; EmitString(o, getClangFullVersion()) << '\n'; o << " <key>files</key>\n" " <array>\n"; for (SmallVectorImpl<FileID>::iterator I=Fids.begin(), E=Fids.end(); I!=E; ++I) { o << " "; EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n'; } o << " </array>\n" " <key>diagnostics</key>\n" " <array>\n"; for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), DE = Diags.end(); DI!=DE; ++DI) { o << " <dict>\n" " <key>path</key>\n"; const PathDiagnostic *D = *DI; o << " <array>\n"; for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end(); I != E; ++I) ReportDiag(o, **I, FM, *SM, LangOpts); o << " </array>\n"; // Output the bug type and bug category. o << " <key>description</key>"; EmitString(o, D->getShortDescription()) << '\n'; o << " <key>category</key>"; EmitString(o, D->getCategory()) << '\n'; o << " <key>type</key>"; EmitString(o, D->getBugType()) << '\n'; // Output information about the semantic context where // the issue occurred. if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { // FIXME: handle blocks, which have no name. if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { StringRef declKind; switch (ND->getKind()) { case Decl::CXXRecord: declKind = "C++ class"; break; case Decl::CXXMethod: declKind = "C++ method"; break; case Decl::ObjCMethod: declKind = "Objective-C method"; break; case Decl::Function: declKind = "function"; break; default: break; } if (!declKind.empty()) { const std::string &declName = ND->getDeclName().getAsString(); o << " <key>issue_context_kind</key>"; EmitString(o, declKind) << '\n'; o << " <key>issue_context</key>"; EmitString(o, declName) << '\n'; } // Output the bug hash for issue unique-ing. Currently, it's just an // offset from the beginning of the function. if (const Stmt *Body = DeclWithIssue->getBody()) { // If the bug uniqueing location exists, use it for the hash. // For example, this ensures that two leaks reported on the same line // will have different issue_hashes and that the hash will identify // the leak location even after code is added between the allocation // site and the end of scope (leak report location). PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); if (UPDLoc.isValid()) { FullSourceLoc UL(SM->getExpansionLoc(UPDLoc.asLocation()), *SM); FullSourceLoc UFunL(SM->getExpansionLoc( D->getUniqueingDecl()->getBody()->getLocStart()), *SM); o << " <key>issue_hash</key><string>" << UL.getExpansionLineNumber() - UFunL.getExpansionLineNumber() << "</string>\n"; // Otherwise, use the location on which the bug is reported. } else { FullSourceLoc L(SM->getExpansionLoc(D->getLocation().asLocation()), *SM); FullSourceLoc FunL(SM->getExpansionLoc(Body->getLocStart()), *SM); o << " <key>issue_hash</key><string>" << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() << "</string>\n"; } } } } // Output the location of the bug. o << " <key>location</key>\n"; EmitLocation(o, *SM, LangOpts, D->getLocation().asLocation(), FM, 2); // Output the diagnostic to the sub-diagnostic client, if any. if (!filesMade->empty()) { StringRef lastName; PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); if (files) { for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), CE = files->end(); CI != CE; ++CI) { StringRef newName = CI->first; if (newName != lastName) { if (!lastName.empty()) { o << " </array>\n"; } lastName = newName; o << " <key>" << lastName << "_files</key>\n"; o << " <array>\n"; } o << " <string>" << CI->second << "</string>\n"; } o << " </array>\n"; } } // Close up the entry. o << " </dict>\n"; } o << " </array>\n"; // Finish. o << "</dict>\n</plist>"; }
void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) { StringRef Data; if (error(Section.getContents(Data))) return; SmallVector<StringRef, 10> FunctionNames; StringMap<StringRef> FunctionLineTables; ListScope D(W, "CodeViewDebugInfo"); { // FIXME: Add more offset correctness checks. DataExtractor DE(Data, true, 4); uint32_t Offset = 0, Magic = DE.getU32(&Offset); W.printHex("Magic", Magic); if (Magic != COFF::DEBUG_SECTION_MAGIC) { error(object_error::parse_failed); return; } bool Finished = false; while (DE.isValidOffset(Offset) && !Finished) { // The section consists of a number of subsection in the following format: // |Type|PayloadSize|Payload...| uint32_t SubSectionType = DE.getU32(&Offset), PayloadSize = DE.getU32(&Offset); ListScope S(W, "Subsection"); W.printHex("Type", SubSectionType); W.printHex("PayloadSize", PayloadSize); if (PayloadSize > Data.size() - Offset) { error(object_error::parse_failed); return; } StringRef Contents = Data.substr(Offset, PayloadSize); if (opts::CodeViewSubsectionBytes) { // Print the raw contents to simplify debugging if anything goes wrong // afterwards. W.printBinaryBlock("Contents", Contents); } switch (SubSectionType) { case COFF::DEBUG_SYMBOL_SUBSECTION: if (opts::SectionSymbols) printCodeViewSymbolsSubsection(Contents, Section, Offset); break; case COFF::DEBUG_LINE_TABLE_SUBSECTION: { // Holds a PC to file:line table. Some data to parse this subsection is // stored in the other subsections, so just check sanity and store the // pointers for deferred processing. if (PayloadSize < 12) { // There should be at least three words to store two function // relocations and size of the code. error(object_error::parse_failed); return; } StringRef FunctionName; if (error(resolveSymbolName(Obj->getCOFFSection(Section), Offset, FunctionName))) return; W.printString("FunctionName", FunctionName); if (FunctionLineTables.count(FunctionName) != 0) { // Saw debug info for this function already? error(object_error::parse_failed); return; } FunctionLineTables[FunctionName] = Contents; FunctionNames.push_back(FunctionName); break; } case COFF::DEBUG_STRING_TABLE_SUBSECTION: if (PayloadSize == 0 || CVStringTable.data() != nullptr || Contents.back() != '\0') { // Empty or duplicate or non-null-terminated subsection. error(object_error::parse_failed); return; } CVStringTable = Contents; break; case COFF::DEBUG_INDEX_SUBSECTION: // Holds the translation table from file indices // to offsets in the string table. if (PayloadSize == 0 || CVFileIndexToStringOffsetTable.data() != nullptr) { // Empty or duplicate subsection. error(object_error::parse_failed); return; } CVFileIndexToStringOffsetTable = Contents; break; } Offset += PayloadSize; // Align the reading pointer by 4. Offset += (-Offset) % 4; } } // Dump the line tables now that we've read all the subsections and know all // the required information. for (unsigned I = 0, E = FunctionNames.size(); I != E; ++I) { StringRef Name = FunctionNames[I]; ListScope S(W, "FunctionLineTable"); W.printString("FunctionName", Name); DataExtractor DE(FunctionLineTables[Name], true, 4); uint32_t Offset = 8; // Skip relocations. uint32_t FunctionSize = DE.getU32(&Offset); W.printHex("CodeSize", FunctionSize); while (DE.isValidOffset(Offset)) { // For each range of lines with the same filename, we have a segment // in the line table. The filename string is accessed using double // indirection to the string table subsection using the index subsection. uint32_t OffsetInIndex = DE.getU32(&Offset), SegmentLength = DE.getU32(&Offset), FullSegmentSize = DE.getU32(&Offset); if (FullSegmentSize != 12 + 8 * SegmentLength) { error(object_error::parse_failed); return; } uint32_t FilenameOffset; { DataExtractor SDE(CVFileIndexToStringOffsetTable, true, 4); uint32_t OffsetInSDE = OffsetInIndex; if (!SDE.isValidOffset(OffsetInSDE)) { error(object_error::parse_failed); return; } FilenameOffset = SDE.getU32(&OffsetInSDE); } if (FilenameOffset == 0 || FilenameOffset + 1 >= CVStringTable.size() || CVStringTable.data()[FilenameOffset - 1] != '\0') { // Each string in an F3 subsection should be preceded by a null // character. error(object_error::parse_failed); return; } StringRef Filename(CVStringTable.data() + FilenameOffset); ListScope S(W, "FilenameSegment"); W.printString("Filename", Filename); for (unsigned J = 0; J != SegmentLength && DE.isValidOffset(Offset); ++J) { // Then go the (PC, LineNumber) pairs. The line number is stored in the // least significant 31 bits of the respective word in the table. uint32_t PC = DE.getU32(&Offset), LineNumber = DE.getU32(&Offset) & 0x7fffffff; if (PC >= FunctionSize) { error(object_error::parse_failed); return; } char Buffer[32]; format("+0x%X", PC).snprint(Buffer, 32); W.printNumber(Buffer, LineNumber); } } } }
void Preprocessor::HandlePragmaIncludeAlias(Token &Tok) { // We will either get a quoted filename or a bracketed filename, and we // have to track which we got. The first filename is the source name, // and the second name is the mapped filename. If the first is quoted, // the second must be as well (cannot mix and match quotes and brackets). // Get the open paren Lex(Tok); if (Tok.isNot(tok::l_paren)) { Diag(Tok, diag::warn_pragma_include_alias_expected) << "("; return; } // We expect either a quoted string literal, or a bracketed name Token SourceFilenameTok; CurPPLexer->LexIncludeFilename(SourceFilenameTok); if (SourceFilenameTok.is(tok::eod)) { // The diagnostic has already been handled return; } StringRef SourceFileName; SmallString<128> FileNameBuffer; if (SourceFilenameTok.is(tok::string_literal) || SourceFilenameTok.is(tok::angle_string_literal)) { SourceFileName = getSpelling(SourceFilenameTok, FileNameBuffer); } else if (SourceFilenameTok.is(tok::less)) { // This could be a path instead of just a name FileNameBuffer.push_back('<'); SourceLocation End; if (ConcatenateIncludeName(FileNameBuffer, End)) return; // Diagnostic already emitted SourceFileName = FileNameBuffer.str(); } else { Diag(Tok, diag::warn_pragma_include_alias_expected_filename); return; } FileNameBuffer.clear(); // Now we expect a comma, followed by another include name Lex(Tok); if (Tok.isNot(tok::comma)) { Diag(Tok, diag::warn_pragma_include_alias_expected) << ","; return; } Token ReplaceFilenameTok; CurPPLexer->LexIncludeFilename(ReplaceFilenameTok); if (ReplaceFilenameTok.is(tok::eod)) { // The diagnostic has already been handled return; } StringRef ReplaceFileName; if (ReplaceFilenameTok.is(tok::string_literal) || ReplaceFilenameTok.is(tok::angle_string_literal)) { ReplaceFileName = getSpelling(ReplaceFilenameTok, FileNameBuffer); } else if (ReplaceFilenameTok.is(tok::less)) { // This could be a path instead of just a name FileNameBuffer.push_back('<'); SourceLocation End; if (ConcatenateIncludeName(FileNameBuffer, End)) return; // Diagnostic already emitted ReplaceFileName = FileNameBuffer.str(); } else { Diag(Tok, diag::warn_pragma_include_alias_expected_filename); return; } // Finally, we expect the closing paren Lex(Tok); if (Tok.isNot(tok::r_paren)) { Diag(Tok, diag::warn_pragma_include_alias_expected) << ")"; return; } // Now that we have the source and target filenames, we need to make sure // they're both of the same type (angled vs non-angled) StringRef OriginalSource = SourceFileName; bool SourceIsAngled = GetIncludeFilenameSpelling(SourceFilenameTok.getLocation(), SourceFileName); bool ReplaceIsAngled = GetIncludeFilenameSpelling(ReplaceFilenameTok.getLocation(), ReplaceFileName); if (!SourceFileName.empty() && !ReplaceFileName.empty() && (SourceIsAngled != ReplaceIsAngled)) { unsigned int DiagID; if (SourceIsAngled) DiagID = diag::warn_pragma_include_alias_mismatch_angle; else DiagID = diag::warn_pragma_include_alias_mismatch_quote; Diag(SourceFilenameTok.getLocation(), DiagID) << SourceFileName << ReplaceFileName; return; } // Now we can let the include handler know about this mapping getHeaderSearchInfo().AddIncludeAlias(OriginalSource, ReplaceFileName); }
void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, const SectionRef &Section, uint32_t OffsetInSection) { if (Subsection.size() == 0) { error(object_error::parse_failed); return; } DataExtractor DE(Subsection, true, 4); uint32_t Offset = 0; // Function-level subsections have "procedure start" and "procedure end" // commands that should come in pairs and surround relevant info. bool InFunctionScope = false; while (DE.isValidOffset(Offset)) { // Read subsection segments one by one. uint16_t Size = DE.getU16(&Offset); // The section size includes the size of the type identifier. if (Size < 2 || !DE.isValidOffsetForDataOfSize(Offset, Size)) { error(object_error::parse_failed); return; } Size -= 2; uint16_t Type = DE.getU16(&Offset); switch (Type) { case COFF::DEBUG_SYMBOL_TYPE_PROC_START: { DictScope S(W, "ProcStart"); if (InFunctionScope || Size < 36) { error(object_error::parse_failed); return; } InFunctionScope = true; // We're currently interested in a limited subset of fields in this // segment, just ignore the rest of the fields for now. uint8_t Unused[12]; DE.getU8(&Offset, Unused, 12); uint32_t CodeSize = DE.getU32(&Offset); DE.getU8(&Offset, Unused, 12); StringRef SectionName; if (error(resolveSymbolName(Obj->getCOFFSection(Section), OffsetInSection + Offset, SectionName))) return; Offset += 4; DE.getU8(&Offset, Unused, 3); StringRef DisplayName = DE.getCStr(&Offset); if (!DE.isValidOffset(Offset)) { error(object_error::parse_failed); return; } W.printString("DisplayName", DisplayName); W.printString("Section", SectionName); W.printHex("CodeSize", CodeSize); break; } case COFF::DEBUG_SYMBOL_TYPE_PROC_END: { W.startLine() << "ProcEnd\n"; if (!InFunctionScope || Size > 0) { error(object_error::parse_failed); return; } InFunctionScope = false; break; } default: { if (opts::CodeViewSubsectionBytes) { ListScope S(W, "Record"); W.printHex("Size", Size); W.printHex("Type", Type); StringRef Contents = DE.getData().substr(Offset, Size); W.printBinaryBlock("Contents", Contents); } Offset += Size; break; } } } if (InFunctionScope) error(object_error::parse_failed); }
bool StringRef::getAsInteger(unsigned Radix, APInt &Result) const { StringRef Str = *this; // Autosense radix if not specified. if (Radix == 0) Radix = GetAutoSenseRadix(Str); assert(Radix > 1 && Radix <= 36); // Empty strings (after the radix autosense) are invalid. if (Str.empty()) return true; // Skip leading zeroes. This can be a significant improvement if // it means we don't need > 64 bits. while (!Str.empty() && Str.front() == '0') Str = Str.substr(1); // If it was nothing but zeroes.... if (Str.empty()) { Result = APInt(64, 0); return false; } // (Over-)estimate the required number of bits. unsigned Log2Radix = 0; while ((1U << Log2Radix) < Radix) Log2Radix++; bool IsPowerOf2Radix = ((1U << Log2Radix) == Radix); unsigned BitWidth = Log2Radix * Str.size(); if (BitWidth < Result.getBitWidth()) BitWidth = Result.getBitWidth(); // don't shrink the result else if (BitWidth > Result.getBitWidth()) Result = Result.zext(BitWidth); APInt RadixAP, CharAP; // unused unless !IsPowerOf2Radix if (!IsPowerOf2Radix) { // These must have the same bit-width as Result. RadixAP = APInt(BitWidth, Radix); CharAP = APInt(BitWidth, 0); } // Parse all the bytes of the string given this radix. Result = 0; while (!Str.empty()) { unsigned CharVal; if (Str[0] >= '0' && Str[0] <= '9') CharVal = Str[0]-'0'; else if (Str[0] >= 'a' && Str[0] <= 'z') CharVal = Str[0]-'a'+10; else if (Str[0] >= 'A' && Str[0] <= 'Z') CharVal = Str[0]-'A'+10; else return true; // If the parsed value is larger than the integer radix, the string is // invalid. if (CharVal >= Radix) return true; // Add in this character. if (IsPowerOf2Radix) { Result <<= Log2Radix; Result |= CharVal; } else { Result *= RadixAP; CharAP = CharVal; Result += CharAP; } Str = Str.substr(1); } return false; }
void ExecutionEngine::addGlobalMapping(StringRef Name, uint64_t Addr) { MutexGuard locked(lock); assert(!Name.empty() && "Empty GlobalMapping symbol name!"); DEBUG(dbgs() << "JIT: Map \'" << Name << "\' to [" << Addr << "]\n";);
unsigned llvm::AArch64::checkArchVersion(StringRef Arch) { if (Arch.size() >= 2 && Arch[0] == 'v' && std::isdigit(Arch[1])) return (Arch[1] - 48); return 0; }
void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { if (!BT) { BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " "Objective-C pointer types")); ASTContext &Ctx = C.getASTContext(); arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); dictionaryWithObjectsAndKeysS = GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); } if (!isVariadicMessage(msg)) return; // We are not interested in the selector arguments since they have // well-defined types, so the compiler will issue a warning for them. unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); // We're not interested in the last argument since it has to be nil or the // compiler would have issued a warning for it elsewhere. unsigned variadicArgsEnd = msg.getNumArgs() - 1; if (variadicArgsEnd <= variadicArgsBegin) return; // Verify that all arguments have Objective-C types. llvm::Optional<ExplodedNode*> errorNode; ProgramStateRef state = C.getState(); for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { QualType ArgTy = msg.getArgExpr(I)->getType(); if (ArgTy->isObjCObjectPointerType()) continue; // Block pointers are treaded as Objective-C pointers. if (ArgTy->isBlockPointerType()) continue; // Ignore pointer constants. if (isa<loc::ConcreteInt>(msg.getArgSVal(I))) continue; // Ignore pointer types annotated with 'NSObject' attribute. if (C.getASTContext().isObjCNSObjectType(ArgTy)) continue; // Ignore CF references, which can be toll-free bridged. if (coreFoundation::isCFObjectRef(ArgTy)) continue; // Generate only one error node to use for all bug reports. if (!errorNode.hasValue()) errorNode = C.addTransition(); if (!errorNode.getValue()) continue; SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); StringRef TypeName = GetReceiverInterfaceName(msg); if (!TypeName.empty()) os << "Argument to '" << TypeName << "' method '"; else os << "Argument to method '"; os << msg.getSelector().getAsString() << "' should be an Objective-C pointer type, not '"; ArgTy.print(os, C.getLangOpts()); os << "'"; BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); R->addRange(msg.getArgSourceRange(I)); C.emitReport(R); } }