本文整理汇总了C++中PresumedLoc::isValid方法的典型用法代码示例。如果您正苦于以下问题:C++ PresumedLoc::isValid方法的具体用法?C++ PresumedLoc::isValid怎么用?C++ PresumedLoc::isValid使用的例子?那么, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在类PresumedLoc
的用法示例。
在下文中一共展示了PresumedLoc::isValid方法的13个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的C++代码示例。
示例1: getFileAndLine
static void getFileAndLine(CodeGenFunction &CGF, SourceLocation Loc,
llvm::SmallVectorImpl<llvm::Value*> *Out) {
PresumedLoc PLoc = CGF.CGM.getContext().getSourceManager().getPresumedLoc(Loc);
llvm::Constant *File =
CGF.CGM.GetAddrOfConstantCString(PLoc.isValid()? PLoc.getFilename() : "(unknown)");
Out->push_back(CGF.Builder.CreateConstInBoundsGEP2_32(File, 0, 0));
Out->push_back(llvm::ConstantInt::get(CGF.IntTy, PLoc.isValid()? PLoc.getLine() : 0));
}
示例2: emitIncludeLocation
void TextDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) {
if (DiagOpts->ShowLocation && PLoc.isValid())
OS << "In file included from " << PLoc.getFilename() << ':'
<< PLoc.getLine() << ":\n";
else
OS << "In included file:\n";
}
示例3: printStructBefore
void TypePrinter::printStructBefore(const StructType *T, raw_ostream &OS) {
//if (Policy.SuppressTag)
//return;
//bool HasKindDecoration = false;
// We don't print tags unless this is an elaborated type.
// In C, we just assume every RecordType is an elaborated type.
//if (!(Policy.LangOpts.CPlusPlus || Policy.SuppressTagKeyword ||
//D->getTypedefNameForAnonDecl())) {
//HasKindDecoration = true;
//OS << D->getKindName();
//OS << ' ';
//}
// Compute the full nested-name-specifier for this type.
// In C, this will always be empty except when the type
// being printed is anonymous within other Record.
//if (!Policy.SuppressScope)
//AppendScope(D->getDeclContext(), OS);
//if (const IdentifierInfo *II = D->getIdentifier())
//OS << II->getName();
//else if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) {
//assert(Typedef->getIdentifier() && "Typedef without identifier?");
//OS << Typedef->getIdentifier()->getName();
//} else {
// Make an unambiguous representation for anonymous types, e.g.
// <anonymous enum at /usr/include/string.h:120:9>
//if (isa<CXXRecordDecl>(D) && cast<CXXRecordDecl>(D)->isLambda()) {
//OS << "<lambda";
//HasKindDecoration = true;
//} else {
//OS << "<anonymous";
//}
//if (Policy.AnonymousTagLocations) {
// Suppress the redundant tag keyword if we just printed one.
// We don't have to worry about ElaboratedTypes here because you can't
// refer to an anonymous type with one.
//if (!HasKindDecoration)
OS << "<struct";
PresumedLoc PLoc =
PresumedLoc::build(T->getDecl()->getASTContext().getSourceManager(),
T->getDecl()->getLocation());
if (PLoc.isValid()) {
OS << " at " << PLoc.getFilename()
<< ':' << PLoc.getLine()
<< ':' << PLoc.getColumn();
}
//}
OS << '>';
//}
spaceBeforePlaceHolder(OS);
}
示例4: emitImportLocation
void TextDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
StringRef ModuleName) {
if (DiagOpts->ShowLocation && PLoc.isValid())
OS << "In module '" << ModuleName << "' imported from "
<< PLoc.getFilename() << ':' << PLoc.getLine() << ":\n";
else
OS << "In module '" << ModuleName << "':\n";
}
示例5: emitBuildingModuleLocation
void TextDiagnostic::emitBuildingModuleLocation(SourceLocation Loc,
PresumedLoc PLoc,
StringRef ModuleName,
const SourceManager &SM) {
if (DiagOpts->ShowLocation && PLoc.isValid())
OS << "While building module '" << ModuleName << "' imported from "
<< PLoc.getFilename() << ':' << PLoc.getLine() << ":\n";
else
OS << "While building module '" << ModuleName << "':\n";
}
示例6:
llvm::MDNode *SanitizerMetadata::getLocationMetadata(SourceLocation Loc) {
PresumedLoc PLoc = CGM.getContext().getSourceManager().getPresumedLoc(Loc);
if (!PLoc.isValid())
return nullptr;
llvm::LLVMContext &VMContext = CGM.getLLVMContext();
llvm::Value *LocMetadata[] = {
llvm::MDString::get(VMContext, PLoc.getFilename()),
llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), PLoc.getLine()),
llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext),
PLoc.getColumn()),
};
return llvm::MDNode::get(VMContext, LocMetadata);
}
示例7: emitImportLocation
void DiagnosticNoteRenderer::emitImportLocation(SourceLocation Loc,
PresumedLoc PLoc,
StringRef ModuleName,
const SourceManager &SM) {
// Generate a note indicating the include location.
SmallString<200> MessageStorage;
llvm::raw_svector_ostream Message(MessageStorage);
Message << "in module '" << ModuleName;
if (PLoc.isValid())
Message << "' imported from " << PLoc.getFilename() << ':'
<< PLoc.getLine();
Message << ":";
emitNote(Loc, Message.str(), &SM);
}
示例8: getInclusions
static void getInclusions(const SrcMgr::SLocEntry &(SourceManager::*Getter)(unsigned, bool*) const, unsigned n,
CXTranslationUnit TU, CXInclusionVisitor CB,
CXClientData clientData)
{
ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
SourceManager &SM = CXXUnit->getSourceManager();
ASTContext &Ctx = CXXUnit->getASTContext();
SmallVector<CXSourceLocation, 10> InclusionStack;
const bool HasPreamble = SM.getPreambleFileID().isValid();
for (unsigned i = 0 ; i < n ; ++i) {
bool Invalid = false;
const SrcMgr::SLocEntry &SL = (SM.*Getter)(i, &Invalid);
if (!SL.isFile() || Invalid)
continue;
const SrcMgr::FileInfo &FI = SL.getFile();
if (!FI.getContentCache()->OrigEntry)
continue;
// If this is the main file, and there is a preamble, skip this SLoc. The
// inclusions of the preamble already showed it.
SourceLocation L = FI.getIncludeLoc();
if (HasPreamble && CXXUnit->isInMainFileID(L))
continue;
// Build the inclusion stack.
InclusionStack.clear();
while (L.isValid()) {
PresumedLoc PLoc = SM.getPresumedLoc(L);
InclusionStack.push_back(cxloc::translateSourceLocation(Ctx, L));
L = PLoc.isValid()? PLoc.getIncludeLoc() : SourceLocation();
}
// If there is a preamble, the last entry is the "inclusion" of that
// preamble into the main file, which has the bogus entry of main.c:1:1
if (HasPreamble && !InclusionStack.empty())
InclusionStack.pop_back();
// Callback to the client.
// FIXME: We should have a function to construct CXFiles.
CB(static_cast<CXFile>(
const_cast<FileEntry *>(FI.getContentCache()->OrigEntry)),
InclusionStack.data(), InclusionStack.size(), clientData);
}
}
示例9: clang_getInclusions
void clang_getInclusions(CXTranslationUnit TU, CXInclusionVisitor CB,
CXClientData clientData) {
ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU->TUData);
SourceManager &SM = CXXUnit->getSourceManager();
ASTContext &Ctx = CXXUnit->getASTContext();
llvm::SmallVector<CXSourceLocation, 10> InclusionStack;
unsigned i = SM.sloc_loaded_entry_size();
unsigned n = SM.sloc_entry_size();
// In the case where all the SLocEntries are in an external source, traverse
// those SLocEntries as well. This is the case where we are looking
// at the inclusion stack of an AST/PCH file.
if (i >= n)
i = 0;
for ( ; i < n ; ++i) {
const SrcMgr::SLocEntry &SL = SM.getSLocEntry(i);
if (!SL.isFile())
continue;
const SrcMgr::FileInfo &FI = SL.getFile();
if (!FI.getContentCache()->OrigEntry)
continue;
// Build the inclusion stack.
SourceLocation L = FI.getIncludeLoc();
InclusionStack.clear();
while (L.isValid()) {
PresumedLoc PLoc = SM.getPresumedLoc(L);
InclusionStack.push_back(cxloc::translateSourceLocation(Ctx, L));
L = PLoc.isValid()? PLoc.getIncludeLoc() : SourceLocation();
}
// Callback to the client.
// FIXME: We should have a function to construct CXFiles.
CB((CXFile) FI.getContentCache()->OrigEntry,
InclusionStack.data(), InclusionStack.size(), clientData);
}
}
示例10: ExpandBuiltinMacro
/// ExpandBuiltinMacro - If an identifier token is read that is to be expanded
/// as a builtin macro, handle it and return the next token as 'Tok'.
void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
// Figure out which token this is.
IdentifierInfo *II = Tok.getIdentifierInfo();
assert(II && "Can't be a macro without id info!");
// If this is an _Pragma or Microsoft __pragma directive, expand it,
// invoke the pragma handler, then lex the token after it.
if (II == Ident_Pragma)
return Handle_Pragma(Tok);
else if (II == Ident__pragma) // in non-MS mode this is null
return HandleMicrosoft__pragma(Tok);
++NumBuiltinMacroExpanded;
llvm::SmallString<128> TmpBuffer;
llvm::raw_svector_ostream OS(TmpBuffer);
// Set up the return result.
Tok.setIdentifierInfo(0);
Tok.clearFlag(Token::NeedsCleaning);
if (II == Ident__LINE__) {
// C99 6.10.8: "__LINE__: The presumed line number (within the current
// source file) of the current source line (an integer constant)". This can
// be affected by #line.
SourceLocation Loc = Tok.getLocation();
// Advance to the location of the first _, this might not be the first byte
// of the token if it starts with an escaped newline.
Loc = AdvanceToTokenCharacter(Loc, 0);
// One wrinkle here is that GCC expands __LINE__ to location of the *end* of
// a macro expansion. This doesn't matter for object-like macros, but
// can matter for a function-like macro that expands to contain __LINE__.
// Skip down through expansion points until we find a file loc for the
// end of the expansion history.
Loc = SourceMgr.getExpansionRange(Loc).second;
PresumedLoc PLoc = SourceMgr.getPresumedLoc(Loc);
// __LINE__ expands to a simple numeric value.
OS << (PLoc.isValid()? PLoc.getLine() : 1);
Tok.setKind(tok::numeric_constant);
} else if (II == Ident__FILE__ || II == Ident__BASE_FILE__) {
// C99 6.10.8: "__FILE__: The presumed name of the current source file (a
// character string literal)". This can be affected by #line.
PresumedLoc PLoc = SourceMgr.getPresumedLoc(Tok.getLocation());
// __BASE_FILE__ is a GNU extension that returns the top of the presumed
// #include stack instead of the current file.
if (II == Ident__BASE_FILE__ && PLoc.isValid()) {
SourceLocation NextLoc = PLoc.getIncludeLoc();
while (NextLoc.isValid()) {
PLoc = SourceMgr.getPresumedLoc(NextLoc);
if (PLoc.isInvalid())
break;
NextLoc = PLoc.getIncludeLoc();
}
}
// Escape this filename. Turn '\' -> '\\' '"' -> '\"'
llvm::SmallString<128> FN;
if (PLoc.isValid()) {
FN += PLoc.getFilename();
Lexer::Stringify(FN);
OS << '"' << FN.str() << '"';
}
Tok.setKind(tok::string_literal);
} else if (II == Ident__DATE__) {
if (!DATELoc.isValid())
ComputeDATE_TIME(DATELoc, TIMELoc, *this);
Tok.setKind(tok::string_literal);
Tok.setLength(strlen("\"Mmm dd yyyy\""));
Tok.setLocation(SourceMgr.createExpansionLoc(DATELoc, Tok.getLocation(),
Tok.getLocation(),
Tok.getLength()));
return;
} else if (II == Ident__TIME__) {
if (!TIMELoc.isValid())
ComputeDATE_TIME(DATELoc, TIMELoc, *this);
Tok.setKind(tok::string_literal);
Tok.setLength(strlen("\"hh:mm:ss\""));
Tok.setLocation(SourceMgr.createExpansionLoc(TIMELoc, Tok.getLocation(),
Tok.getLocation(),
Tok.getLength()));
return;
} else if (II == Ident__INCLUDE_LEVEL__) {
// Compute the presumed include depth of this token. This can be affected
// by GNU line markers.
unsigned Depth = 0;
PresumedLoc PLoc = SourceMgr.getPresumedLoc(Tok.getLocation());
if (PLoc.isValid()) {
PLoc = SourceMgr.getPresumedLoc(PLoc.getIncludeLoc());
for (; PLoc.isValid(); ++Depth)
PLoc = SourceMgr.getPresumedLoc(PLoc.getIncludeLoc());
}
//.........这里部分代码省略.........
示例11: printTag
void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) {
if (Policy.SuppressTag)
return;
bool HasKindDecoration = false;
// bool SuppressTagKeyword
// = Policy.LangOpts.CPlusPlus || Policy.SuppressTagKeyword;
// We don't print tags unless this is an elaborated type.
// In C, we just assume every RecordType is an elaborated type.
if (!(Policy.LangOpts.CPlusPlus || Policy.SuppressTagKeyword ||
D->getTypedefNameForAnonDecl())) {
HasKindDecoration = true;
OS << D->getKindName();
OS << ' ';
}
// Compute the full nested-name-specifier for this type.
// In C, this will always be empty except when the type
// being printed is anonymous within other Record.
if (!Policy.SuppressScope)
AppendScope(D->getDeclContext(), OS);
if (const IdentifierInfo *II = D->getIdentifier())
OS << II->getName();
else if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) {
assert(Typedef->getIdentifier() && "Typedef without identifier?");
OS << Typedef->getIdentifier()->getName();
} else {
// Make an unambiguous representation for anonymous types, e.g.
// <anonymous enum at /usr/include/string.h:120:9>
if (isa<CXXRecordDecl>(D) && cast<CXXRecordDecl>(D)->isLambda()) {
OS << "<lambda";
HasKindDecoration = true;
} else {
OS << "<anonymous";
}
if (Policy.AnonymousTagLocations) {
// Suppress the redundant tag keyword if we just printed one.
// We don't have to worry about ElaboratedTypes here because you can't
// refer to an anonymous type with one.
if (!HasKindDecoration)
OS << " " << D->getKindName();
PresumedLoc PLoc = D->getASTContext().getSourceManager().getPresumedLoc(
D->getLocation());
if (PLoc.isValid()) {
OS << " at " << PLoc.getFilename()
<< ':' << PLoc.getLine()
<< ':' << PLoc.getColumn();
}
}
OS << '>';
}
// If this is a class template specialization, print the template
// arguments.
if (ClassTemplateSpecializationDecl *Spec
= dyn_cast<ClassTemplateSpecializationDecl>(D)) {
const TemplateArgument *Args;
unsigned NumArgs;
if (TypeSourceInfo *TAW = Spec->getTypeAsWritten()) {
const TemplateSpecializationType *TST =
cast<TemplateSpecializationType>(TAW->getType());
Args = TST->getArgs();
NumArgs = TST->getNumArgs();
} else {
const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs();
Args = TemplateArgs.data();
NumArgs = TemplateArgs.size();
}
IncludeStrongLifetimeRAII Strong(Policy);
TemplateSpecializationType::PrintTemplateArgumentList(OS,
Args, NumArgs,
Policy);
}
spaceBeforePlaceHolder(OS);
}
示例12: HandleDiagnostic
void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level,
const DiagnosticInfo &Info) {
// Default implementation (Warnings/errors count).
DiagnosticClient::HandleDiagnostic(Level, Info);
// Keeps track of the the starting position of the location
// information (e.g., "foo.c:10:4:") that precedes the error
// message. We use this information to determine how long the
// file+line+column number prefix is.
uint64_t StartOfLocationInfo = OS.tell();
if (!Prefix.empty())
OS << Prefix << ": ";
// If the location is specified, print out a file/line/col and include trace
// if enabled.
if (Info.getLocation().isValid()) {
const SourceManager &SM = Info.getSourceManager();
PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation());
if (PLoc.isInvalid()) {
// At least print the file name if available:
FileID FID = SM.getFileID(Info.getLocation());
if (!FID.isInvalid()) {
const FileEntry* FE = SM.getFileEntryForID(FID);
if (FE && FE->getName()) {
OS << FE->getName();
if (FE->getDevice() == 0 && FE->getInode() == 0
&& FE->getFileMode() == 0) {
// in PCH is a guess, but a good one:
OS << " (in PCH)";
}
OS << ": ";
}
}
} else {
unsigned LineNo = PLoc.getLine();
// First, if this diagnostic is not in the main file, print out the
// "included from" lines.
if (LastWarningLoc != PLoc.getIncludeLoc()) {
LastWarningLoc = PLoc.getIncludeLoc();
PrintIncludeStack(LastWarningLoc, SM);
StartOfLocationInfo = OS.tell();
}
// Compute the column number.
if (DiagOpts->ShowLocation && PLoc.isValid()) {
if (DiagOpts->ShowColors)
OS.changeColor(savedColor, true);
// Emit a Visual Studio compatible line number syntax.
if (LangOpts && LangOpts->Microsoft) {
OS << PLoc.getFilename() << '(' << LineNo << ')';
OS << " : ";
} else {
OS << PLoc.getFilename() << ':' << LineNo << ':';
if (DiagOpts->ShowColumn)
if (unsigned ColNo = PLoc.getColumn())
OS << ColNo << ':';
}
if (DiagOpts->ShowSourceRanges && Info.getNumRanges()) {
FileID CaretFileID =
SM.getFileID(SM.getInstantiationLoc(Info.getLocation()));
bool PrintedRange = false;
for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) {
// Ignore invalid ranges.
if (!Info.getRange(i).isValid()) continue;
SourceLocation B = Info.getRange(i).getBegin();
SourceLocation E = Info.getRange(i).getEnd();
B = SM.getInstantiationLoc(B);
E = SM.getInstantiationLoc(E);
// If the End location and the start location are the same and are a
// macro location, then the range was something that came from a
// macro expansion or _Pragma. If this is an object-like macro, the
// best we can do is to highlight the range. If this is a
// function-like macro, we'd also like to highlight the arguments.
if (B == E && Info.getRange(i).getEnd().isMacroID())
E = SM.getInstantiationRange(Info.getRange(i).getEnd()).second;
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
// If the start or end of the range is in another file, just discard
// it.
if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
continue;
// Add in the length of the token, so that we cover multi-char
// tokens.
unsigned TokSize = 0;
if (Info.getRange(i).isTokenRange())
TokSize = Lexer::MeasureTokenLength(E, SM, *LangOpts);
OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':'
<< SM.getColumnNumber(BInfo.first, BInfo.second) << '-'
<< SM.getLineNumber(EInfo.first, EInfo.second) << ':'
<< (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize)
//.........这里部分代码省略.........
示例13: ExpandBuiltinMacro
/// ExpandBuiltinMacro - If an identifier token is read that is to be expanded
/// as a builtin macro, handle it and return the next token as 'Tok'.
void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
// Figure out which token this is.
IdentifierInfo *II = Tok.getIdentifierInfo();
assert(II && "Can't be a macro without id info!");
++NumBuiltinMacroExpanded;
SmallString<128> TmpBuffer;
llvm::raw_svector_ostream OS(TmpBuffer);
// Set up the return result.
Tok.setIdentifierInfo(nullptr);
Tok.clearFlag(Token::NeedsCleaning);
if (II == Ident__LINE__) {
// C99 6.10.8: "__LINE__: The presumed line number (within the current
// source file) of the current source line (an integer constant)". This can
// be affected by #line.
SourceLocation Loc = Tok.getLocation();
// Advance to the location of the first _, this might not be the first byte
// of the token if it starts with an escaped newline.
Loc = AdvanceToTokenCharacter(Loc, 0);
// One wrinkle here is that GCC expands __LINE__ to location of the *end* of
// a macro expansion. This doesn't matter for object-like macros, but
// can matter for a function-like macro that expands to contain __LINE__.
// Skip down through expansion points until we find a file loc for the
// end of the expansion history.
Loc = SourceMgr.getExpansionRange(Loc).getEnd();
PresumedLoc PLoc = SourceMgr.getPresumedLoc(Loc);
// __LINE__ expands to a simple numeric value.
OS << (PLoc.isValid()? PLoc.getLine() : 1);
Tok.setKind(tok::numeric_constant);
} else if (II == Ident__FILE__ || II == Ident__BASE_FILE__) {
// C99 6.10.8: "__FILE__: The presumed name of the current source file (a
// character string literal)". This can be affected by #line.
PresumedLoc PLoc = SourceMgr.getPresumedLoc(Tok.getLocation());
// __BASE_FILE__ is a GNU extension that returns the top of the presumed
// #include stack instead of the current file.
if (II == Ident__BASE_FILE__ && PLoc.isValid()) {
SourceLocation NextLoc = PLoc.getIncludeLoc();
while (NextLoc.isValid()) {
PLoc = SourceMgr.getPresumedLoc(NextLoc);
if (PLoc.isInvalid())
break;
NextLoc = PLoc.getIncludeLoc();
}
}
// Escape this filename. Turn '\' -> '\\' '"' -> '\"'
SmallString<128> FN;
if (PLoc.isValid()) {
FN += PLoc.getFilename();
Lexer::Stringify(FN);
OS << '"' << FN << '"';
}
Tok.setKind(tok::string_literal);
} else if (II == Ident__DATE__) {
Diag(Tok.getLocation(), diag::warn_pp_date_time);
if (!DATELoc.isValid())
ComputeDATE_TIME(DATELoc, TIMELoc, *this);
Tok.setKind(tok::string_literal);
Tok.setLength(strlen("\"Mmm dd yyyy\""));
Tok.setLocation(SourceMgr.createExpansionLoc(DATELoc, Tok.getLocation(),
Tok.getLocation(),
Tok.getLength()));
return;
} else if (II == Ident__TIME__) {
Diag(Tok.getLocation(), diag::warn_pp_date_time);
if (!TIMELoc.isValid())
ComputeDATE_TIME(DATELoc, TIMELoc, *this);
Tok.setKind(tok::string_literal);
Tok.setLength(strlen("\"hh:mm:ss\""));
Tok.setLocation(SourceMgr.createExpansionLoc(TIMELoc, Tok.getLocation(),
Tok.getLocation(),
Tok.getLength()));
return;
} else if (II == Ident__INCLUDE_LEVEL__) {
// Compute the presumed include depth of this token. This can be affected
// by GNU line markers.
unsigned Depth = 0;
PresumedLoc PLoc = SourceMgr.getPresumedLoc(Tok.getLocation());
if (PLoc.isValid()) {
PLoc = SourceMgr.getPresumedLoc(PLoc.getIncludeLoc());
for (; PLoc.isValid(); ++Depth)
PLoc = SourceMgr.getPresumedLoc(PLoc.getIncludeLoc());
}
// __INCLUDE_LEVEL__ expands to a simple numeric value.
OS << Depth;
Tok.setKind(tok::numeric_constant);
} else if (II == Ident__TIMESTAMP__) {
//.........这里部分代码省略.........