/// ParseDirective - Go through the comment and see if it indicates expected /// diagnostics. If so, then put them in the appropriate directive list. /// /// Returns true if any valid directives were found. static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, SourceLocation Pos, DiagnosticsEngine &Diags, VerifyDiagnosticConsumer::DirectiveStatus &Status) { // A single comment may contain multiple directives. bool FoundDirective = false; for (ParseHelper PH(S); !PH.Done();) { // Search for token: expected if (!PH.Search("expected", true)) break; PH.Advance(); // Next token: - if (!PH.Next("-")) continue; PH.Advance(); // Next token: { error | warning | note } DirectiveList* DL = NULL; if (PH.Next("error")) DL = ED ? &ED->Errors : NULL; else if (PH.Next("warning")) DL = ED ? &ED->Warnings : NULL; else if (PH.Next("note")) DL = ED ? &ED->Notes : NULL; else if (PH.Next("no-diagnostics")) { if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) Diags.Report(Pos, diag::err_verify_invalid_no_diags) << /*IsExpectedNoDiagnostics=*/true; else Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; continue; } else continue; PH.Advance(); if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) { Diags.Report(Pos, diag::err_verify_invalid_no_diags) << /*IsExpectedNoDiagnostics=*/false; continue; } Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives; // If a directive has been found but we're not interested // in storing the directive information, return now. if (!DL) return true; // Default directive kind. bool RegexKind = false; const char* KindStr = "string"; // Next optional token: - if (PH.Next("-re")) { PH.Advance(); RegexKind = true; KindStr = "regex"; } // Next optional token: @ SourceLocation ExpectedLoc; if (!PH.Next("@")) { ExpectedLoc = Pos; } else { PH.Advance(); unsigned Line = 0; bool FoundPlus = PH.Next("+"); if (FoundPlus || PH.Next("-")) { // Relative to current line. PH.Advance(); bool Invalid = false; unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { if (FoundPlus) ExpectedLine += Line; else ExpectedLine -= Line; ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); } } else { // Absolute line number. if (PH.Next(Line) && Line > 0) ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); } if (ExpectedLoc.isInvalid()) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_missing_line) << KindStr; continue; } PH.Advance(); } // Skip optional whitespace. PH.SkipWhitespace(); // Next optional token: positive integer or a '+'. unsigned Min = 1; unsigned Max = 1; if (PH.Next(Min)) { PH.Advance(); // A positive integer can be followed by a '+' meaning min // or more, or by a '-' meaning a range from min to max. if (PH.Next("+")) { Max = Directive::MaxCount; PH.Advance(); } else if (PH.Next("-")) { PH.Advance(); if (!PH.Next(Max) || Max < Min) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_invalid_range) << KindStr; continue; } PH.Advance(); } else { Max = Min; } } else if (PH.Next("+")) { // '+' on its own means "1 or more". Max = Directive::MaxCount; PH.Advance(); } // Skip optional whitespace. PH.SkipWhitespace(); // Next token: {{ if (!PH.Next("{{")) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_missing_start) << KindStr; continue; } PH.Advance(); const char* const ContentBegin = PH.C; // mark content begin // Search for token: }} if (!PH.Search("}}")) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_missing_end) << KindStr; continue; } const char* const ContentEnd = PH.P; // mark content end PH.Advance(); // Build directive text; convert \n to newlines. std::string Text; StringRef NewlineStr = "\\n"; StringRef Content(ContentBegin, ContentEnd-ContentBegin); size_t CPos = 0; size_t FPos; while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { Text += Content.substr(CPos, FPos-CPos); Text += '\n'; CPos = FPos + NewlineStr.size(); } if (Text.empty()) Text.assign(ContentBegin, ContentEnd); // Construct new directive. Directive *D = Directive::create(RegexKind, Pos, ExpectedLoc, Text, Min, Max); std::string Error; if (D->isValid(Error)) { DL->push_back(D); FoundDirective = true; } else { Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), diag::err_verify_invalid_content) << KindStr << Error; } } return FoundDirective; }
/// Finish - This does final analysis of the declspec, rejecting things like /// "_Imaginary" (lacking an FP type). This returns a diagnostic to issue or /// diag::NUM_DIAGNOSTICS if there is no error. After calling this method, /// DeclSpec is guaranteed self-consistent, even if an error occurred. void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) { // Before possibly changing their values, save specs as written. SaveWrittenBuiltinSpecs(); SaveStorageSpecifierAsWritten(); // Check the type specifier components first. // Validate and finalize AltiVec vector declspec. if (TypeAltiVecVector) { if (TypeAltiVecBool) { // Sign specifiers are not allowed with vector bool. (PIM 2.1) if (TypeSpecSign != TSS_unspecified) { Diag(D, TSSLoc, diag::err_invalid_vector_bool_decl_spec) << getSpecifierName((TSS)TypeSpecSign); } // Only char/int are valid with vector bool. (PIM 2.1) if (((TypeSpecType != TST_unspecified) && (TypeSpecType != TST_char) && (TypeSpecType != TST_int)) || TypeAltiVecPixel) { Diag(D, TSTLoc, diag::err_invalid_vector_bool_decl_spec) << (TypeAltiVecPixel ? "__pixel" : getSpecifierName((TST)TypeSpecType)); } // Only 'short' is valid with vector bool. (PIM 2.1) if ((TypeSpecWidth != TSW_unspecified) && (TypeSpecWidth != TSW_short)) Diag(D, TSWLoc, diag::err_invalid_vector_bool_decl_spec) << getSpecifierName((TSW)TypeSpecWidth); // Elements of vector bool are interpreted as unsigned. (PIM 2.1) if ((TypeSpecType == TST_char) || (TypeSpecType == TST_int) || (TypeSpecWidth != TSW_unspecified)) TypeSpecSign = TSS_unsigned; } if (TypeAltiVecPixel) { //TODO: perform validation TypeSpecType = TST_int; TypeSpecSign = TSS_unsigned; TypeSpecWidth = TSW_short; TypeSpecOwned = false; } } // signed/unsigned are only valid with int/char/wchar_t. if (TypeSpecSign != TSS_unspecified) { if (TypeSpecType == TST_unspecified) TypeSpecType = TST_int; // unsigned -> unsigned int, signed -> signed int. else if (TypeSpecType != TST_int && TypeSpecType != TST_int128 && TypeSpecType != TST_char && TypeSpecType != TST_wchar) { Diag(D, TSSLoc, diag::err_invalid_sign_spec) << getSpecifierName((TST)TypeSpecType); // signed double -> double. TypeSpecSign = TSS_unspecified; } } // Validate the width of the type. switch (TypeSpecWidth) { case TSW_unspecified: break; case TSW_short: // short int case TSW_longlong: // long long int if (TypeSpecType == TST_unspecified) TypeSpecType = TST_int; // short -> short int, long long -> long long int. else if (TypeSpecType != TST_int) { Diag(D, TSWLoc, TypeSpecWidth == TSW_short ? diag::err_invalid_short_spec : diag::err_invalid_longlong_spec) << getSpecifierName((TST)TypeSpecType); TypeSpecType = TST_int; TypeSpecOwned = false; } break; case TSW_long: // long double, long int if (TypeSpecType == TST_unspecified) TypeSpecType = TST_int; // long -> long int. else if (TypeSpecType != TST_int && TypeSpecType != TST_double) { Diag(D, TSWLoc, diag::err_invalid_long_spec) << getSpecifierName((TST)TypeSpecType); TypeSpecType = TST_int; TypeSpecOwned = false; } break; } // TODO: if the implementation does not implement _Complex or _Imaginary, // disallow their use. Need information about the backend. if (TypeSpecComplex != TSC_unspecified) { if (TypeSpecType == TST_unspecified) { Diag(D, TSCLoc, diag::ext_plain_complex) << FixItHint::CreateInsertion( PP.getLocForEndOfToken(getTypeSpecComplexLoc()), " double"); TypeSpecType = TST_double; // _Complex -> _Complex double. } else if (TypeSpecType == TST_int || TypeSpecType == TST_char) { // Note that this intentionally doesn't include _Complex _Bool. if (!PP.getLangOpts().CPlusPlus) Diag(D, TSTLoc, diag::ext_integer_complex); } else if (TypeSpecType != TST_float && TypeSpecType != TST_double) { Diag(D, TSCLoc, diag::err_invalid_complex_spec) << getSpecifierName((TST)TypeSpecType); TypeSpecComplex = TSC_unspecified; } } // If no type specifier was provided and we're parsing a language where // the type specifier is not optional, but we got 'auto' as a storage // class specifier, then assume this is an attempt to use C++0x's 'auto' // type specifier. // FIXME: Does Microsoft really support implicit int in C++? if (PP.getLangOpts().CPlusPlus && !PP.getLangOpts().MicrosoftExt && TypeSpecType == TST_unspecified && StorageClassSpec == SCS_auto) { TypeSpecType = TST_auto; StorageClassSpec = StorageClassSpecAsWritten = SCS_unspecified; TSTLoc = TSTNameLoc = StorageClassSpecLoc; StorageClassSpecLoc = SourceLocation(); } // Diagnose if we've recovered from an ill-formed 'auto' storage class // specifier in a pre-C++0x dialect of C++. if (!PP.getLangOpts().CPlusPlus0x && TypeSpecType == TST_auto) Diag(D, TSTLoc, diag::ext_auto_type_specifier); if (PP.getLangOpts().CPlusPlus && !PP.getLangOpts().CPlusPlus0x && StorageClassSpec == SCS_auto) Diag(D, StorageClassSpecLoc, diag::warn_auto_storage_class) << FixItHint::CreateRemoval(StorageClassSpecLoc); if (TypeSpecType == TST_char16 || TypeSpecType == TST_char32) Diag(D, TSTLoc, diag::warn_cxx98_compat_unicode_type) << (TypeSpecType == TST_char16 ? "char16_t" : "char32_t"); if (Constexpr_specified) Diag(D, ConstexprLoc, diag::warn_cxx98_compat_constexpr); // C++ [class.friend]p6: // No storage-class-specifier shall appear in the decl-specifier-seq // of a friend declaration. if (isFriendSpecified() && getStorageClassSpec()) { DeclSpec::SCS SC = getStorageClassSpec(); const char *SpecName = getSpecifierName(SC); SourceLocation SCLoc = getStorageClassSpecLoc(); SourceLocation SCEndLoc = SCLoc.getLocWithOffset(strlen(SpecName)); Diag(D, SCLoc, diag::err_friend_storage_spec) << SpecName << FixItHint::CreateRemoval(SourceRange(SCLoc, SCEndLoc)); ClearStorageClassSpecs(); } assert(!TypeSpecOwned || isDeclRep((TST) TypeSpecType)); // Okay, now we can infer the real type. // TODO: return "auto function" and other bad things based on the real type. // 'data definition has no type or storage class'? }
// Add the input to the memory buffer, parse it, and add it to the AST. IncrementalParser::EParseResult IncrementalParser::Parse(llvm::StringRef input) { Preprocessor& PP = m_CI->getPreprocessor(); DiagnosticConsumer& DClient = m_CI->getDiagnosticClient(); PP.enableIncrementalProcessing(); DClient.BeginSourceFile(m_CI->getLangOpts(), &PP); // Reset the transaction information getLastTransaction().setBeforeFirstDecl(getCI()->getSema().CurContext); if (input.size()) { std::ostringstream source_name; source_name << "input_line_" << (m_MemoryBuffer.size() + 1); llvm::MemoryBuffer* MB = llvm::MemoryBuffer::getMemBufferCopy(input, source_name.str()); m_MemoryBuffer.push_back(MB); SourceManager& SM = getCI()->getSourceManager(); // Create SourceLocation, which will allow clang to order the overload // candidates for example SourceLocation NewLoc = SM.getLocForStartOfFile(m_VirtualFileID); NewLoc = NewLoc.getLocWithOffset(m_MemoryBuffer.size() + 1); // Create FileID for the current buffer FileID FID = SM.createFileIDForMemBuffer(m_MemoryBuffer.back(), /*LoadedID*/0, /*LoadedOffset*/0, NewLoc); PP.EnterSourceFile(FID, 0, NewLoc); } Parser::DeclGroupPtrTy ADecl; while (!m_Parser->ParseTopLevelDecl(ADecl)) { // Not end of file. // If we got a null return and something *was* parsed, ignore it. This // is due to a top-level semicolon, an action override, or a parse error // skipping something. if (ADecl) { DeclGroupRef DGR = ADecl.getAsVal<DeclGroupRef>(); for (DeclGroupRef::iterator i=DGR.begin(); i< DGR.end(); ++i) { if (!m_FirstTopLevelDecl) m_FirstTopLevelDecl = *((*i)->getDeclContext()->decls_begin()); m_LastTopLevelDecl = *i; } m_Consumer->HandleTopLevelDecl(DGR); } // ADecl }; // Process any TopLevelDecls generated by #pragma weak. for (llvm::SmallVector<Decl*,2>::iterator I = getCI()->getSema().WeakTopLevelDecls().begin(), E = getCI()->getSema().WeakTopLevelDecls().end(); I != E; ++I) { m_Consumer->HandleTopLevelDecl(DeclGroupRef(*I)); } getCI()->getSema().PerformPendingInstantiations(); DClient.EndSourceFile(); PP.enableIncrementalProcessing(false); DiagnosticsEngine& Diag = getCI()->getSema().getDiagnostics(); if (Diag.hasErrorOccurred()) return IncrementalParser::kFailed; else if (Diag.getNumWarnings()) return IncrementalParser::kSuccessWithWarnings; return IncrementalParser::kSuccess; }
void IncludeOrderPPCallbacks::EndOfMainFile() { LookForMainModule = true; if (IncludeDirectives.empty()) return; // TODO: find duplicated includes. // Form blocks of includes. We don't want to sort across blocks. This also // implicitly makes us never reorder over #defines or #if directives. // FIXME: We should be more careful about sorting below comments as we don't // know if the comment refers to the next include or the whole block that // follows. std::vector<unsigned> Blocks(1, 0); for (unsigned I = 1, E = IncludeDirectives.size(); I != E; ++I) if (SM.getExpansionLineNumber(IncludeDirectives[I].Loc) != SM.getExpansionLineNumber(IncludeDirectives[I - 1].Loc) + 1) Blocks.push_back(I); Blocks.push_back(IncludeDirectives.size()); // Sentinel value. // Get a vector of indices. std::vector<unsigned> IncludeIndices; for (unsigned I = 0, E = IncludeDirectives.size(); I != E; ++I) IncludeIndices.push_back(I); // Sort the includes. We first sort by priority, then lexicographically. for (unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI) std::sort(IncludeIndices.begin() + Blocks[BI], IncludeIndices.begin() + Blocks[BI + 1], [this](unsigned LHSI, unsigned RHSI) { IncludeDirective &LHS = IncludeDirectives[LHSI]; IncludeDirective &RHS = IncludeDirectives[RHSI]; int PriorityLHS = getPriority(LHS.Filename, LHS.IsAngled, LHS.IsMainModule); int PriorityRHS = getPriority(RHS.Filename, RHS.IsAngled, RHS.IsMainModule); return std::tie(PriorityLHS, LHS.Filename) < std::tie(PriorityRHS, RHS.Filename); }); // Emit a warning for each block and fixits for all changes within that block. for (unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI) { // Find the first include that's not in the right position. unsigned I, E; for (I = Blocks[BI], E = Blocks[BI + 1]; I != E; ++I) if (IncludeIndices[I] != I) break; if (I == E) continue; // Emit a warning. auto D = Check.diag(IncludeDirectives[I].Loc, "#includes are not sorted properly"); // Emit fix-its for all following includes in this block. for (; I != E; ++I) { if (IncludeIndices[I] == I) continue; const IncludeDirective &CopyFrom = IncludeDirectives[IncludeIndices[I]]; SourceLocation FromLoc = CopyFrom.Range.getBegin(); const char *FromData = SM.getCharacterData(FromLoc); unsigned FromLen = std::strcspn(FromData, "\n"); StringRef FixedName(FromData, FromLen); SourceLocation ToLoc = IncludeDirectives[I].Range.getBegin(); const char *ToData = SM.getCharacterData(ToLoc); unsigned ToLen = std::strcspn(ToData, "\n"); auto ToRange = CharSourceRange::getCharRange(ToLoc, ToLoc.getLocWithOffset(ToLen)); D << FixItHint::CreateReplacement(ToRange, FixedName); } } IncludeDirectives.clear(); }
bool RewriteUtils::removeArgFromExpr(const Expr *E, int ParamPos) { if (ParamPos >= static_cast<int>(getNumArgsWrapper(E))) return true; const Expr *Arg = getArgWrapper(E, ParamPos); TransAssert(Arg && "Null arg!"); if (dyn_cast<CXXDefaultArgExpr>(Arg->IgnoreParenCasts())) return true; SourceRange ArgRange = Arg->getSourceRange(); int RangeSize = TheRewriter->getRangeSize(ArgRange); if (RangeSize == -1) return false; SourceLocation StartLoc = ArgRange.getBegin(); unsigned int NumArgs = getNumArgsWrapper(E); if ((ParamPos == 0) && ((NumArgs == 1) || ((NumArgs > 1) && dyn_cast<CXXDefaultArgExpr>( getArgWrapper(E, 1)->IgnoreParenCasts())))) { // Note that ')' is included in ParamLocRange return !(TheRewriter->RemoveText(ArgRange)); } int LastArgPos = static_cast<int>(NumArgs - 1); // The param is the last non-default parameter if ((ParamPos == LastArgPos) || ((ParamPos < LastArgPos) && dyn_cast<CXXDefaultArgExpr>( getArgWrapper(E, ParamPos+1)->IgnoreParenCasts()))) { int Offset = 0; const char *StartBuf = SrcManager->getCharacterData(StartLoc); TransAssert(StartBuf && "Invalid start buffer!"); while (*StartBuf != ',') { StartBuf--; Offset--; } SourceLocation NewStartLoc = StartLoc.getLocWithOffset(Offset); return !(TheRewriter->RemoveText(NewStartLoc, RangeSize - Offset)); } // We cannot use SrcManager->getCharacterData(StartLoc) to get the buffer, // because it returns the unmodified string. I've tried to use // getEndlocationUntil(ArgRange, ",", ...) call, but still failed. // Seems in some cases, it returns bad results for a complex case like: // foo(...foo(...), ...) // So I ended up with this ugly way - get the end loc from the next arg. const Expr *NextArg = getArgWrapper(E, ParamPos+1); SourceRange NextArgRange = NextArg->getSourceRange(); SourceLocation NextStartLoc = NextArgRange.getBegin(); const char *NextStartBuf = SrcManager->getCharacterData(NextStartLoc); int Offset = 0; while (*NextStartBuf != ',') { NextStartBuf--; Offset--; } SourceLocation NewEndLoc = NextStartLoc.getLocWithOffset(Offset); return !TheRewriter->RemoveText(SourceRange(StartLoc, NewEndLoc)); }
static bool fillRanges(MemoryBuffer *Code, std::vector<tooling::Range> &Ranges) { IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( new vfs::InMemoryFileSystem); FileManager Files(FileSystemOptions(), InMemoryFileSystem); DiagnosticsEngine Diagnostics( IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), new DiagnosticOptions); SourceManager Sources(Diagnostics, Files); FileID ID = createInMemoryFile("<irrelevant>", Code, Sources, Files, InMemoryFileSystem.get()); if (!LineRanges.empty()) { if (!Offsets.empty() || !Lengths.empty()) { errs() << "error: cannot use -lines with -offset/-length\n"; return true; } for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) { unsigned FromLine, ToLine; if (parseLineRange(LineRanges[i], FromLine, ToLine)) { errs() << "error: invalid <start line>:<end line> pair\n"; return true; } if (FromLine > ToLine) { errs() << "error: start line should be less than end line\n"; return true; } SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1); SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX); if (Start.isInvalid() || End.isInvalid()) return true; unsigned Offset = Sources.getFileOffset(Start); unsigned Length = Sources.getFileOffset(End) - Offset; Ranges.push_back(tooling::Range(Offset, Length)); } return false; } if (Offsets.empty()) Offsets.push_back(0); if (Offsets.size() != Lengths.size() && !(Offsets.size() == 1 && Lengths.empty())) { errs() << "error: number of -offset and -length arguments must match.\n"; return true; } for (unsigned i = 0, e = Offsets.size(); i != e; ++i) { if (Offsets[i] >= Code->getBufferSize()) { errs() << "error: offset " << Offsets[i] << " is outside the file\n"; return true; } SourceLocation Start = Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]); SourceLocation End; if (i < Lengths.size()) { if (Offsets[i] + Lengths[i] > Code->getBufferSize()) { errs() << "error: invalid length " << Lengths[i] << ", offset + length (" << Offsets[i] + Lengths[i] << ") is outside the file.\n"; return true; } End = Start.getLocWithOffset(Lengths[i]); } else { End = Sources.getLocForEndOfFile(ID); } unsigned Offset = Sources.getFileOffset(Start); unsigned Length = Sources.getFileOffset(End) - Offset; Ranges.push_back(tooling::Range(Offset, Length)); } return false; }
/// \brief 'Loc' is the end of a statement range. This returns the location /// immediately after the semicolon following the statement. /// If no semicolon is found or the location is inside a macro, the returned /// source location will be invalid. SourceLocation findLocationAfterSemi(SourceLocation loc, ASTContext &Ctx) { SourceLocation SemiLoc = findSemiAfterLocation(loc, Ctx); if (SemiLoc.isInvalid()) return SourceLocation(); return SemiLoc.getLocWithOffset(1); }
CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const { SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID()); Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset()); assert(Loc.isFileID()); return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length)); }
void TypeRenameTransform::processFunctionDecl(FunctionDecl *D) { // if no type source info, it's a void f(void) function auto TSI = D->getTypeSourceInfo(); if (TSI) { processTypeLoc(TSI->getTypeLoc()); } // handle ctor name initializers if (auto CD = dyn_cast<CXXConstructorDecl>(D)) { auto BL = CD->getLocation(); std::string newName; if (BL.isValid() && CD->getParent()->getLocation() != BL && nameMatches(CD->getParent(), newName, true)) { renameLocation(BL, newName); } for (auto II = CD->init_begin(), IE = CD->init_end(); II != IE; ++II) { if ((*II)->isBaseInitializer()) { processTypeLoc((*II)->getBaseClassLoc()); } auto X = (*II)->getInit(); if (X) { processStmt(X); } } } // dtor if (auto DD = dyn_cast<CXXDestructorDecl>(D)) { // if parent matches auto BL = DD->getLocation(); std::string newName; auto P = DD->getParent(); if (BL.isValid() && P->getLocation() != BL && nameMatches(P, newName, true)) { // can't use renameLocation since this is a tricky case // need to use raw_identifier because Lexer::findLocationAfterToken // performs a raw lexing SourceLocation EL = Lexer::findLocationAfterToken(BL, tok::raw_identifier, sema->getSourceManager(), sema->getLangOpts(), false); // TODO: Find the right way to do this -- consider this a hack // llvm::errs() << indent() << "dtor at: " << loc(BL) << ", locStart: " << loc(DD->getLocStart()) // << ", nameAsString: " << P->getNameAsString() << ", len: " << P->getNameAsString().size() // << ", DD nameAsString: " << DD->getNameAsString() << ", len: " << DD->getNameAsString().size() // << "\n"; if (EL.isValid()) { // EL is 1 char after the dtor name ~Foo, so -1 == pos of 'o' SourceLocation NE = EL.getLocWithOffset(-1); // we use the parent's name to see how much we should back off // if we use D->getNameAsString(), we'd run into two problems: // (1) the name would be ~Foo // (2) the name for template class dtor would be ~Foo<T> SourceLocation NB = EL.getLocWithOffset(-(int)P->getNameAsString().size()); if (NB.isMacroID()) { // TODO: emit error llvm::errs() << "Warning: Token is resulted from macro expansion" " and is therefore not renamed, at: " << loc(NB) << "\n"; } else { // TODO: Determine if it's a wrtiable file // TODO: Determine if the location has already been touched or // needs skipping (such as in refactoring API user's code, then // the API headers need no changing since later the new API will be // in place) replace(SourceRange(NB, NE), newName); } } } } // rename the params' types for (auto PI = D->param_begin(), PE = D->param_end(); PI != PE; ++PI) { processParmVarDecl(*PI); } // the name itself processQualifierLoc(D->getQualifierLoc()); // handle body if (auto B = D->getBody()) { if (stmtInSameFileAsDecl(B, D)) { // llvm::errs() << indent() << "body? " << D->getBody() << "\n"; processStmt(B); } } }
SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const { SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID()); Loc = Loc.getLocWithOffset(Offset.getOffset()); assert(Loc.isFileID()); return Loc; }
CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const { SourceLocation Loc = getFileLocation(SM); return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length)); }
clang::StmtResult InsiemeSema::ActOnCompoundStmt(clang::SourceLocation L, clang::SourceLocation R, llvm::ArrayRef<clang::Stmt*> Elts, bool isStmtExpr) { // we parse the original code segment, within the original locations StmtResult&& ret = Sema::ActOnCompoundStmt(L, R, std::move(Elts), isStmtExpr); clang::CompoundStmt* CS = cast<clang::CompoundStmt>(ret.get()); // This is still buggy as of Clang 3.6.2: // when pragmas are just after the beginning of a compound stmt, example: // { // #pragma xxx // ... // } // the location of the opening bracket is wrong because of a bug in the clang parser. // // We solve the problem by searching for the bracket in the input stream and overwrite // the value of L (which contains the wrong location) with the correct value. enum { MacroIDBit = 1U << 31 }; // from clang/Basic/SourceLocation.h for use with cpp classes { SourceLocation&& leftBracketLoc = SourceMgr.getImmediateSpellingLoc(L); std::pair<FileID, unsigned>&& locInfo = SourceMgr.getDecomposedLoc(leftBracketLoc); llvm::StringRef&& buffer = SourceMgr.getBufferData(locInfo.first); const char* strData = buffer.begin() + locInfo.second; char const* lBracePos = strbchr(strData, buffer.begin(), '{'); // We know the location of the left bracket, we overwrite the value of L with the correct location // but only if the location is valid as in getFileLocWithOffset() in SourceLocation if((((leftBracketLoc.getRawEncoding() & ~MacroIDBit) + (lBracePos - strData)) & MacroIDBit) == 0) { L = leftBracketLoc.getLocWithOffset(lBracePos - strData); } } // For the right bracket, we start at the final statement in the compound // (or its start if it is empty) and search forward until we find the first "}" // Otherwise, cases such as this: // // { // bla(); // } // #pragma test expect_ir(R"( {} )") // // will be broken { SourceLocation rightBracketLoc; if(CS->size() == 0) { rightBracketLoc = SourceMgr.getImmediateSpellingLoc(L); } else { rightBracketLoc = SourceMgr.getImmediateSpellingLoc(CS->body_back()->getLocEnd()); } std::pair<FileID, unsigned>&& locInfo = SourceMgr.getDecomposedLoc(rightBracketLoc); llvm::StringRef buffer = SourceMgr.getBufferData(locInfo.first); const char* strData = buffer.begin() + locInfo.second; char const* rBracePos = strchr(strData, '}'); // We know the location of the right bracket, we overwrite the value of R with the correct location if((((rightBracketLoc.getRawEncoding() & ~MacroIDBit) + (rBracePos - strData)) & MacroIDBit) == 0) { R = rightBracketLoc.getLocWithOffset(rBracePos - strData); } } // the source range we inspect is defined by the new source locations, // this fix the problem with boundaries jumping to the beginning of the file in // the macro expansions: // // #define F(x) { } // // ... // // F(r) // <-this statement will jum to the macro location // PragmaList matched; SourceRange SR(L, R); // for each of the pragmas in the range between brackets for(PragmaFilter&& filter = PragmaFilter(SR, SourceMgr, pimpl->pending_pragma); *filter; ++filter) { PragmaPtr P = *filter; unsigned int pragmaStart = utils::Line(P->getStartLocation(), SourceMgr); unsigned int pragmaEnd = utils::Line(P->getEndLocation(), SourceMgr); bool found = false; // problem with first pragma, compound start is delayed until fist usable line (first stmt) if(CS->size() > 0) { for(clang::CompoundStmt::body_iterator it = CS->body_begin(); it != CS->body_end(); ++it) { unsigned int stmtStart = (Line((*it)->getLocStart(), SourceMgr)); if((pragmaEnd <= stmtStart)) { // ACHTUNG: if the node is a nullStmt, and is not at the end of the compound (in // which case is most probably ours) we can not trust it. semantics wont change, // we move one more. (BUG: nullStmt followed by pragmas, the source begin is // postponed until next stmt) this makes pragmas to be attached to a previous // stmt if(!llvm::isa<clang::NullStmt>(*it)) { // this pragma is attached to the current stmt P->setStatement(*it); matched.push_back(P); found = true; break; } } } } if(!found && pragmaStart <= utils::Line(R, SourceMgr)) { // this is a de-attached pragma (barrier i.e.) at the end of the compound // we need to create a fake NullStmt ( ; ) to attach this Stmt** stmts = new Stmt*[CS->size() + 1]; ArrayRef<clang::Stmt*> stmtList(stmts, CS->size() + 1); clang::CompoundStmt* newCS = new(Context) clang::CompoundStmt(Context, stmtList, CS->getSourceRange().getBegin(), CS->getSourceRange().getEnd()); std::copy(CS->body_begin(), CS->body_end(), newCS->body_begin()); std::for_each(CS->body_begin(), CS->body_end(), [&](Stmt*& curr) { this->Context.Deallocate(curr); }); newCS->setLastStmt(new(Context) NullStmt(SourceLocation())); P->setStatement(*newCS->body_rbegin()); matched.push_back(P); // transfer the ownership of the statement clang::CompoundStmt* oldStmt = ret.getAs<clang::CompoundStmt>(); oldStmt->setStmts(Context, NULL, 0); ret = newCS; CS = newCS; // destroy the old compound stmt Context.Deallocate(oldStmt); delete[] stmts; } } // remove matched pragmas EraseMatchedPragmas(pimpl->pending_pragma, matched); return std::move(ret); }
void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) { const NamespaceDecl *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace"); const SourceManager &Sources = *Result.SourceManager; if (!locationsInSameFile(Sources, ND->getLocStart(), ND->getRBraceLoc())) return; // Don't require closing comments for namespaces spanning less than certain // number of lines. unsigned StartLine = Sources.getSpellingLineNumber(ND->getLocStart()); unsigned EndLine = Sources.getSpellingLineNumber(ND->getRBraceLoc()); if (EndLine - StartLine + 1 <= ShortNamespaceLines) return; // Find next token after the namespace closing brace. SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1); SourceLocation Loc = AfterRBrace; Token Tok; // Skip whitespace until we find the next token. while (Lexer::getRawToken(Loc, Tok, Sources, Result.Context->getLangOpts())) { Loc = Loc.getLocWithOffset(1); } if (!locationsInSameFile(Sources, ND->getRBraceLoc(), Loc)) return; bool NextTokenIsOnSameLine = Sources.getSpellingLineNumber(Loc) == EndLine; // If we insert a line comment before the token in the same line, we need // to insert a line break. bool NeedLineBreak = NextTokenIsOnSameLine && Tok.isNot(tok::eof); SourceRange OldCommentRange(AfterRBrace, AfterRBrace); std::string Message = "%0 not terminated with a closing comment"; // Try to find existing namespace closing comment on the same line. if (Tok.is(tok::comment) && NextTokenIsOnSameLine) { StringRef Comment(Sources.getCharacterData(Loc), Tok.getLength()); SmallVector<StringRef, 7> Groups; if (NamespaceCommentPattern.match(Comment, &Groups)) { StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : ""; StringRef Anonymous = Groups.size() > 3 ? Groups[3] : ""; // Check if the namespace in the comment is the same. if ((ND->isAnonymousNamespace() && NamespaceNameInComment.empty()) || (ND->getNameAsString() == NamespaceNameInComment && Anonymous.empty())) { // FIXME: Maybe we need a strict mode, where we always fix namespace // comments with different format. return; } // Otherwise we need to fix the comment. NeedLineBreak = Comment.startswith("/*"); OldCommentRange = SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength())); Message = (llvm::Twine( "%0 ends with a comment that refers to a wrong namespace '") + NamespaceNameInComment + "'").str(); } else if (Comment.startswith("//")) { // Assume that this is an unrecognized form of a namespace closing line // comment. Replace it. NeedLineBreak = false; OldCommentRange = SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength())); Message = "%0 ends with an unrecognized comment"; } // If it's a block comment, just move it to the next line, as it can be // multi-line or there may be other tokens behind it. } std::string NamespaceName = ND->isAnonymousNamespace() ? "anonymous namespace" : ("namespace '" + ND->getNameAsString() + "'"); diag(AfterRBrace, Message) << NamespaceName << FixItHint::CreateReplacement( CharSourceRange::getCharRange(OldCommentRange), std::string(SpacesBeforeComments, ' ') + getNamespaceComment(ND, NeedLineBreak)); diag(ND->getLocation(), "%0 starts here", DiagnosticIDs::Note) << NamespaceName; }
SourceLocation IncrementalParser::getLastMemoryBufferEndLoc() const { const SourceManager& SM = getCI()->getSourceManager(); SourceLocation Result = SM.getLocForStartOfFile(m_VirtualFileID); return Result.getLocWithOffset(m_MemoryBuffers.size() + 1); }
// Add the input to the memory buffer, parse it, and add it to the AST. IncrementalParser::EParseResult IncrementalParser::ParseInternal(llvm::StringRef input) { if (input.empty()) return IncrementalParser::kSuccess; Sema& S = getCI()->getSema(); assert(!(S.getLangOpts().Modules && m_Consumer->getTransaction()->getCompilationOpts() .CodeGenerationForModule) && "CodeGenerationForModule should be removed once modules are available!"); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S); Preprocessor& PP = m_CI->getPreprocessor(); if (!PP.getCurrentLexer()) { PP.EnterSourceFile(m_CI->getSourceManager().getMainFileID(), 0, SourceLocation()); } assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?"); PP.enableIncrementalProcessing(); std::ostringstream source_name; source_name << "input_line_" << (m_MemoryBuffers.size() + 1); // Create an uninitialized memory buffer, copy code in and append "\n" size_t InputSize = input.size(); // don't include trailing 0 // MemBuffer size should *not* include terminating zero llvm::MemoryBuffer* MB = llvm::MemoryBuffer::getNewUninitMemBuffer(InputSize + 1, source_name.str()); char* MBStart = const_cast<char*>(MB->getBufferStart()); memcpy(MBStart, input.data(), InputSize); memcpy(MBStart + InputSize, "\n", 2); m_MemoryBuffers.push_back(MB); SourceManager& SM = getCI()->getSourceManager(); // Create SourceLocation, which will allow clang to order the overload // candidates for example SourceLocation NewLoc = SM.getLocForStartOfFile(m_VirtualFileID); NewLoc = NewLoc.getLocWithOffset(m_MemoryBuffers.size() + 1); // Create FileID for the current buffer FileID FID = SM.createFileIDForMemBuffer(m_MemoryBuffers.back(), SrcMgr::C_User, /*LoadedID*/0, /*LoadedOffset*/0, NewLoc); PP.EnterSourceFile(FID, /*DirLookup*/0, NewLoc); Parser::DeclGroupPtrTy ADecl; while (!m_Parser->ParseTopLevelDecl(ADecl)) { // If we got a null return and something *was* parsed, ignore it. This // is due to a top-level semicolon, an action override, or a parse error // skipping something. if (ADecl) m_Consumer->HandleTopLevelDecl(ADecl.getAsVal<DeclGroupRef>()); }; // Process any TopLevelDecls generated by #pragma weak. for (llvm::SmallVector<Decl*,2>::iterator I = S.WeakTopLevelDecls().begin(), E = S.WeakTopLevelDecls().end(); I != E; ++I) { m_Consumer->HandleTopLevelDecl(DeclGroupRef(*I)); } DiagnosticsEngine& Diag = S.getDiagnostics(); if (Diag.hasErrorOccurred()) return IncrementalParser::kFailed; else if (Diag.getNumWarnings()) return IncrementalParser::kSuccessWithWarnings; return IncrementalParser::kSuccess; }
static void format() { FileManager Files((FileSystemOptions())); DiagnosticsEngine Diagnostics( IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), new DiagnosticOptions); SourceManager Sources(Diagnostics, Files); OwningPtr<MemoryBuffer> Code; if (error_code ec = MemoryBuffer::getFileOrSTDIN(FileName, Code)) { llvm::errs() << ec.message() << "\n"; return; } FileID ID = createInMemoryFile(FileName, Code.get(), Sources, Files); Lexer Lex(ID, Sources.getBuffer(ID), Sources, getFormattingLangOpts()); if (Offsets.empty()) Offsets.push_back(0); if (Offsets.size() != Lengths.size() && !(Offsets.size() == 1 && Lengths.empty())) { llvm::errs() << "Number of -offset and -length arguments must match.\n"; return; } std::vector<CharSourceRange> Ranges; for (cl::list<int>::size_type i = 0, e = Offsets.size(); i != e; ++i) { SourceLocation Start = Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]); SourceLocation End; if (i < Lengths.size()) { End = Start.getLocWithOffset(Lengths[i]); } else { End = Sources.getLocForEndOfFile(ID); } Ranges.push_back(CharSourceRange::getCharRange(Start, End)); } tooling::Replacements Replaces = reformat(getStyle(), Lex, Sources, Ranges); if (OutputXML) { llvm::outs() << "<?xml version='1.0'?>\n<replacements xml:space='preserve'>\n"; for (tooling::Replacements::const_iterator I = Replaces.begin(), E = Replaces.end(); I != E; ++I) { llvm::outs() << "<replacement " << "offset='" << I->getOffset() << "' " << "length='" << I->getLength() << "'>" << I->getReplacementText() << "</replacement>\n"; } llvm::outs() << "</replacements>\n"; } else { Rewriter Rewrite(Sources, LangOptions()); tooling::applyAllReplacements(Replaces, Rewrite); if (Inplace) { if (Replaces.size() == 0) return; // Nothing changed, don't touch the file. std::string ErrorInfo; llvm::raw_fd_ostream FileStream(FileName.c_str(), ErrorInfo, llvm::raw_fd_ostream::F_Binary); if (!ErrorInfo.empty()) { llvm::errs() << "Error while writing file: " << ErrorInfo << "\n"; return; } Rewrite.getEditBuffer(ID).write(FileStream); FileStream.flush(); } else { Rewrite.getEditBuffer(ID).write(outs()); } } }
void html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, FileID FID, const char *title) { const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FID); const char* FileStart = Buf->getBufferStart(); const char* FileEnd = Buf->getBufferEnd(); SourceLocation StartLoc = R.getSourceMgr().getLocForStartOfFile(FID); SourceLocation EndLoc = StartLoc.getLocWithOffset(FileEnd-FileStart); std::string s; llvm::raw_string_ostream os(s); os << "<!doctype html>\n" // Use HTML 5 doctype "<html>\n<head>\n"; if (title) os << "<title>" << html::EscapeText(title) << "</title>\n"; os << "<style type=\"text/css\">\n" " body { color:#000000; background-color:#ffffff }\n" " body { font-family:Helvetica, sans-serif; font-size:10pt }\n" " h1 { font-size:14pt }\n" " .code { border-collapse:collapse; width:100%; }\n" " .code { font-family: \"Monospace\", monospace; font-size:10pt }\n" " .code { line-height: 1.2em }\n" " .comment { color: green; font-style: oblique }\n" " .keyword { color: blue }\n" " .string_literal { color: red }\n" " .directive { color: darkmagenta }\n" // Macro expansions. " .expansion { display: none; }\n" " .macro:hover .expansion { display: block; border: 2px solid #FF0000; " "padding: 2px; background-color:#FFF0F0; font-weight: normal; " " -webkit-border-radius:5px; -webkit-box-shadow:1px 1px 7px #000; " " border-radius:5px; box-shadow:1px 1px 7px #000; " "position: absolute; top: -1em; left:10em; z-index: 1 } \n" " .macro { color: darkmagenta; background-color:LemonChiffon;" // Macros are position: relative to provide base for expansions. " position: relative }\n" " .num { width:2.5em; padding-right:2ex; background-color:#eeeeee }\n" " .num { text-align:right; font-size:8pt }\n" " .num { color:#444444 }\n" " .line { padding-left: 1ex; border-left: 3px solid #ccc }\n" " .line { white-space: pre }\n" " .msg { -webkit-box-shadow:1px 1px 7px #000 }\n" " .msg { box-shadow:1px 1px 7px #000 }\n" " .msg { -webkit-border-radius:5px }\n" " .msg { border-radius:5px }\n" " .msg { font-family:Helvetica, sans-serif; font-size:8pt }\n" " .msg { float:left }\n" " .msg { padding:0.25em 1ex 0.25em 1ex }\n" " .msg { margin-top:10px; margin-bottom:10px }\n" " .msg { font-weight:bold }\n" " .msg { max-width:60em; word-wrap: break-word; white-space: pre-wrap }\n" " .msgT { padding:0x; spacing:0x }\n" " .msgEvent { background-color:#fff8b4; color:#000000 }\n" " .msgControl { background-color:#bbbbbb; color:#000000 }\n" " .mrange { background-color:#dfddf3 }\n" " .mrange { border-bottom:1px solid #6F9DBE }\n" " .PathIndex { font-weight: bold; padding:0px 5px; " "margin-right:5px; }\n" " .PathIndex { -webkit-border-radius:8px }\n" " .PathIndex { border-radius:8px }\n" " .PathIndexEvent { background-color:#bfba87 }\n" " .PathIndexControl { background-color:#8c8c8c }\n" " .PathNav a { text-decoration:none; font-size: larger }\n" " .CodeInsertionHint { font-weight: bold; background-color: #10dd10 }\n" " .CodeRemovalHint { background-color:#de1010 }\n" " .CodeRemovalHint { border-bottom:1px solid #6F9DBE }\n" " table.simpletable {\n" " padding: 5px;\n" " font-size:12pt;\n" " margin:20px;\n" " border-collapse: collapse; border-spacing: 0px;\n" " }\n" " td.rowname {\n" " text-align:right; font-weight:bold; color:#444444;\n" " padding-right:2ex; }\n" "</style>\n</head>\n<body>"; // Generate header R.InsertTextBefore(StartLoc, os.str()); // Generate footer R.InsertTextAfter(EndLoc, "</body></html>\n"); }
/// ParseDirective - Go through the comment and see if it indicates expected /// diagnostics. If so, then put them in the appropriate directive list. /// /// Returns true if any valid directives were found. static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, Preprocessor *PP, SourceLocation Pos, VerifyDiagnosticConsumer::DirectiveStatus &Status, VerifyDiagnosticConsumer::MarkerTracker &Markers) { DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); // First, scan the comment looking for markers. for (ParseHelper PH(S); !PH.Done();) { if (!PH.Search("#", true)) break; PH.C = PH.P; if (!PH.NextMarker()) { PH.Next("#"); PH.Advance(); continue; } PH.Advance(); Markers.addMarker(PH.Match(), Pos); } // A single comment may contain multiple directives. bool FoundDirective = false; for (ParseHelper PH(S); !PH.Done();) { // Search for the initial directive token. // If one prefix, save time by searching only for its directives. // Otherwise, search for any potential directive token and check it later. const auto &Prefixes = Diags.getDiagnosticOptions().VerifyPrefixes; if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(), true, true) : PH.Search("", true, true))) break; StringRef DToken = PH.Match(); PH.Advance(); // Default directive kind. UnattachedDirective D; const char *KindStr = "string"; // Parse the initial directive token in reverse so we can easily determine // its exact actual prefix. If we were to parse it from the front instead, // it would be harder to determine where the prefix ends because there // might be multiple matching -verify prefixes because some might prefix // others. // Regex in initial directive token: -re if (DToken.endswith("-re")) { D.RegexKind = true; KindStr = "regex"; DToken = DToken.substr(0, DToken.size()-3); } // Type in initial directive token: -{error|warning|note|no-diagnostics} bool NoDiag = false; StringRef DType; if (DToken.endswith(DType="-error")) D.DL = ED ? &ED->Errors : nullptr; else if (DToken.endswith(DType="-warning")) D.DL = ED ? &ED->Warnings : nullptr; else if (DToken.endswith(DType="-remark")) D.DL = ED ? &ED->Remarks : nullptr; else if (DToken.endswith(DType="-note")) D.DL = ED ? &ED->Notes : nullptr; else if (DToken.endswith(DType="-no-diagnostics")) { NoDiag = true; if (D.RegexKind) continue; } else continue; DToken = DToken.substr(0, DToken.size()-DType.size()); // What's left in DToken is the actual prefix. That might not be a -verify // prefix even if there is only one -verify prefix (for example, the full // DToken is foo-bar-warning, but foo is the only -verify prefix). if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken)) continue; if (NoDiag) { if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) Diags.Report(Pos, diag::err_verify_invalid_no_diags) << /*IsExpectedNoDiagnostics=*/true; else Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; continue; } if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) { Diags.Report(Pos, diag::err_verify_invalid_no_diags) << /*IsExpectedNoDiagnostics=*/false; continue; } Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives; // If a directive has been found but we're not interested // in storing the directive information, return now. if (!D.DL) return true; // Next optional token: @ SourceLocation ExpectedLoc; StringRef Marker; bool MatchAnyLine = false; if (!PH.Next("@")) { ExpectedLoc = Pos; } else { PH.Advance(); unsigned Line = 0; bool FoundPlus = PH.Next("+"); if (FoundPlus || PH.Next("-")) { // Relative to current line. PH.Advance(); bool Invalid = false; unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { if (FoundPlus) ExpectedLine += Line; else ExpectedLine -= Line; ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); } } else if (PH.Next(Line)) { // Absolute line number. if (Line > 0) ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); } else if (PH.NextMarker()) { Marker = PH.Match(); } else if (PP && PH.Search(":")) { // Specific source file. StringRef Filename(PH.C, PH.P-PH.C); PH.Advance(); // Lookup file via Preprocessor, like a #include. const DirectoryLookup *CurDir; const FileEntry *FE = PP->LookupFile(Pos, Filename, false, nullptr, nullptr, CurDir, nullptr, nullptr, nullptr, nullptr, nullptr); if (!FE) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_missing_file) << Filename << KindStr; continue; } if (SM.translateFile(FE).isInvalid()) SM.createFileID(FE, Pos, SrcMgr::C_User); if (PH.Next(Line) && Line > 0) ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); else if (PH.Next("*")) { MatchAnyLine = true; ExpectedLoc = SM.translateFileLineCol(FE, 1, 1); } } else if (PH.Next("*")) { MatchAnyLine = true; ExpectedLoc = SourceLocation(); } if (ExpectedLoc.isInvalid() && !MatchAnyLine && Marker.empty()) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_missing_line) << KindStr; continue; } PH.Advance(); } // Skip optional whitespace. PH.SkipWhitespace(); // Next optional token: positive integer or a '+'. if (PH.Next(D.Min)) { PH.Advance(); // A positive integer can be followed by a '+' meaning min // or more, or by a '-' meaning a range from min to max. if (PH.Next("+")) { D.Max = Directive::MaxCount; PH.Advance(); } else if (PH.Next("-")) { PH.Advance(); if (!PH.Next(D.Max) || D.Max < D.Min) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_invalid_range) << KindStr; continue; } PH.Advance(); } else { D.Max = D.Min; } } else if (PH.Next("+")) { // '+' on its own means "1 or more". D.Max = Directive::MaxCount; PH.Advance(); } // Skip optional whitespace. PH.SkipWhitespace(); // Next token: {{ if (!PH.Next("{{")) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_missing_start) << KindStr; continue; } PH.Advance(); const char* const ContentBegin = PH.C; // mark content begin // Search for token: }} if (!PH.SearchClosingBrace("{{", "}}")) { Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), diag::err_verify_missing_end) << KindStr; continue; } const char* const ContentEnd = PH.P; // mark content end PH.Advance(); D.DirectivePos = Pos; D.ContentBegin = Pos.getLocWithOffset(ContentBegin - PH.Begin); // Build directive text; convert \n to newlines. StringRef NewlineStr = "\\n"; StringRef Content(ContentBegin, ContentEnd-ContentBegin); size_t CPos = 0; size_t FPos; while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { D.Text += Content.substr(CPos, FPos-CPos); D.Text += '\n'; CPos = FPos + NewlineStr.size(); } if (D.Text.empty()) D.Text.assign(ContentBegin, ContentEnd); // Check that regex directives contain at least one regex. if (D.RegexKind && D.Text.find("{{") == StringRef::npos) { Diags.Report(D.ContentBegin, diag::err_verify_missing_regex) << D.Text; return false; } if (Marker.empty()) attachDirective(Diags, D, ExpectedLoc, MatchAnyLine); else Markers.addDirective(Marker, D); FoundDirective = true; } return FoundDirective; }
/// AvoidConcat - If printing PrevTok immediately followed by Tok would cause /// the two individual tokens to be lexed as a single token, return true /// (which causes a space to be printed between them). This allows the output /// of -E mode to be lexed to the same token stream as lexing the input /// directly would. /// /// This code must conservatively return true if it doesn't want to be 100% /// accurate. This will cause the output to include extra space characters, /// but the resulting output won't have incorrect concatenations going on. /// Examples include "..", which we print with a space between, because we /// don't want to track enough to tell "x.." from "...". bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok, const Token &PrevTok, const Token &Tok) const { // First, check to see if the tokens were directly adjacent in the original // source. If they were, it must be okay to stick them together: if there // were an issue, the tokens would have been lexed differently. SourceManager &SM = PP.getSourceManager(); SourceLocation PrevSpellLoc = SM.getSpellingLoc(PrevTok.getLocation()); SourceLocation SpellLoc = SM.getSpellingLoc(Tok.getLocation()); if (PrevSpellLoc.getLocWithOffset(PrevTok.getLength()) == SpellLoc) return false; tok::TokenKind PrevKind = PrevTok.getKind(); if (!PrevTok.isAnnotation() && PrevTok.getIdentifierInfo()) PrevKind = tok::identifier; // Language keyword or named operator. // Look up information on when we should avoid concatenation with prevtok. unsigned ConcatInfo = TokenInfo[PrevKind]; // If prevtok never causes a problem for anything after it, return quickly. if (ConcatInfo == 0) return false; if (ConcatInfo & aci_avoid_equal) { // If the next token is '=' or '==', avoid concatenation. if (Tok.isOneOf(tok::equal, tok::equalequal)) return true; ConcatInfo &= ~aci_avoid_equal; } if (Tok.isAnnotation()) { // Modules annotation can show up when generated automatically for includes. assert(Tok.isOneOf(tok::annot_module_include, tok::annot_module_begin, tok::annot_module_end) && "unexpected annotation in AvoidConcat"); ConcatInfo = 0; } if (ConcatInfo == 0) return false; // Basic algorithm: we look at the first character of the second token, and // determine whether it, if appended to the first token, would form (or // would contribute) to a larger token if concatenated. char FirstChar = 0; if (ConcatInfo & aci_custom) { // If the token does not need to know the first character, don't get it. } else { FirstChar = GetFirstChar(PP, Tok); } switch (PrevKind) { default: llvm_unreachable("InitAvoidConcatTokenInfo built wrong"); case tok::raw_identifier: llvm_unreachable("tok::raw_identifier in non-raw lexing mode!"); case tok::string_literal: case tok::wide_string_literal: case tok::utf8_string_literal: case tok::utf16_string_literal: case tok::utf32_string_literal: case tok::char_constant: case tok::wide_char_constant: case tok::utf8_char_constant: case tok::utf16_char_constant: case tok::utf32_char_constant: if (!PP.getLangOpts().CPlusPlus11) return false; // In C++11, a string or character literal followed by an identifier is a // single token. if (Tok.getIdentifierInfo()) return true; // A ud-suffix is an identifier. If the previous token ends with one, treat // it as an identifier. if (!PrevTok.hasUDSuffix()) return false; // FALL THROUGH. case tok::identifier: // id+id or id+number or id+L"foo". // id+'.'... will not append. if (Tok.is(tok::numeric_constant)) return GetFirstChar(PP, Tok) != '.'; if (Tok.getIdentifierInfo() || Tok.isOneOf(tok::wide_string_literal, tok::utf8_string_literal, tok::utf16_string_literal, tok::utf32_string_literal, tok::wide_char_constant, tok::utf8_char_constant, tok::utf16_char_constant, tok::utf32_char_constant)) return true; // If this isn't identifier + string, we're done. if (Tok.isNot(tok::char_constant) && Tok.isNot(tok::string_literal)) return false; // Otherwise, this is a narrow character or string. If the *identifier* // is a literal 'L', 'u8', 'u' or 'U', avoid pasting L "foo" -> L"foo". return IsIdentifierStringPrefix(PrevTok); case tok::numeric_constant: return isPreprocessingNumberBody(FirstChar) || FirstChar == '+' || FirstChar == '-'; case tok::period: // ..., .*, .1234 return (FirstChar == '.' && PrevPrevTok.is(tok::period)) || isDigit(FirstChar) || (PP.getLangOpts().CPlusPlus && FirstChar == '*'); case tok::amp: // && return FirstChar == '&'; case tok::plus: // ++ return FirstChar == '+'; case tok::minus: // --, ->, ->* return FirstChar == '-' || FirstChar == '>'; case tok::slash: //, /*, // return FirstChar == '*' || FirstChar == '/'; case tok::less: // <<, <<=, <:, <% return FirstChar == '<' || FirstChar == ':' || FirstChar == '%'; case tok::greater: // >>, >>= return FirstChar == '>'; case tok::pipe: // || return FirstChar == '|'; case tok::percent: // %>, %: return FirstChar == '>' || FirstChar == ':'; case tok::colon: // ::, :> return FirstChar == '>' || (PP.getLangOpts().CPlusPlus && FirstChar == ':'); case tok::hash: // ##, #@, %:%: return FirstChar == '#' || FirstChar == '@' || FirstChar == '%'; case tok::arrow: // ->* return PP.getLangOpts().CPlusPlus && FirstChar == '*'; } }
virtual void run(const MatchFinder::MatchResult &Result){ const BinaryOperator* BO = Result.Nodes.getNodeAs<BinaryOperator>("malloc"); #ifdef DEBUG llvm::errs()<<"----BinaryOperator(malloc) find----\n"; #endif if(!BO) return ; const SourceManager *SM = Result.SourceManager; SourceLocation locEnd = BO->getLocEnd(); checkPoint cp; //得到插装位置,找到mallocVarMatcher之前对应匹配到的信息(其实可以不用MallocVarMatcher,MallocVarMatcher只能匹配到纯粹的指针(不带*的)) std::string str_locEnd = locEnd.printToString(*SM); loc_strToint(cp.row,cp.col,str_locEnd.c_str()); bool findFlag = false; int findI; #ifdef DEBUG llvm::errs() <<"binary loc:" <<"|"<<cp.row<<"|"<<cp.col<<"\n"; #endif for(unsigned i=0;i<cpVec.size();++i){ if(cpVec[i].row == cp.row){ findFlag = true; findI = i; break; } } //左子树得到的 = 的左边 Expr * lhs = BO->getLHS(); cp.name = rewrite.ConvertToString((Stmt*)lhs); QualType qt = lhs->getType(); #ifdef DEBUG llvm::errs()<<"lhs cp.name :"<<cp.name<<"\n"; llvm::errs()<<"lhs type :"<<qt.getAsString()<<"\n"; lhs->dump(); #endif //找到的话直接用 if(findFlag){ const NamedDecl *ND = ((DeclRefExpr*)lhs)->getFoundDecl(); std::string str_decl = ND->getNameAsString(); cp.declName = str_decl; SourceLocation declLocStart = ND->getLocStart(); std::string str_declLocStart = declLocStart.printToString(*SM); loc_strToint(cp.declRow,cp.declCol,str_declLocStart.c_str()); }else{//没找到的话,添加进来 cp.declName = cp.name; cp.declRow = cp.row; cp.declCol = cp.col; } //string + 不支持int类型,所以先换成char* char buf[4][32]; sprintf(buf[0],"%d",cp.row); sprintf(buf[1],"%d",cp.col); sprintf(buf[2],"%d",cp.declRow); sprintf(buf[3],"%d",cp.declCol); //将程序运行时的指针值存下来 %x std::string str_insert = "\n{\n\tFILE *fp = fopen(\"" + checkDataFileName +"\",\"a\");\n" "\tif(fp == NULL){\n" + "\t\tprintf(\"fail to open file " + checkDataFileName + "!\\n\");\n" + "\t\texit(-1);\n" + "\t}\n" + //"\tfprintf(fp,\"m " + cp.name + " " + buf[0] + " " + buf[1] + " " + cp.declName + " " + buf[2] + " " + buf[3] + " %x \\n\"," + cp.name + ");\n" + "\tfprintf(fp,\"m " + cp.name + " " + buf[0] + " " + buf[1] + " %x \\n\"," + cp.name + ");\n" + "\tfclose(fp);\n\n" + "\tint fd=open(FIFO_SERVER,O_WRONLY |O_NONBLOCK,0);\n" + "\tif(fd==-1){perror(\"open\");exit(1);}\n" + "\tchar w_buf[100];\n" + "\tsprintf(w_buf,\"m "+ cp.name + " " + buf[0] + " " + buf[1] + " %x \\n\"," + cp.name + ");\n" + "\tif(write(fd,w_buf,100)==-1){\n" + "\t\tif(errno==EAGAIN)\n" + "\t\t\tprintf(\"The FIFO has not been read yet.Please try later\\n\");\n" + "\t}\n" + "\telse\n" + "\t\tprintf(\"write %s to the FIFO\\n\",w_buf);\n" + "\tsleep(1);\n" + "\tclose(fd);\n" + "}\n"; //llvm::errs() << "-----\n"<<str_insert<<"\n----\n"; //找位置插装 int locOffset = 2; SourceLocation SL_locWithOffset = locEnd.getLocWithOffset(locOffset); rewrite.InsertText(SL_locWithOffset,str_insert.c_str(),true,true); if(!findFlag){ cpVec.push_back(cp); } #ifdef DEBUG llvm::errs()<<"----BinaryOperator(malloc) end----\n"; #endif }
bool VisitFunctionDecl(FunctionDecl *f) { // Only function definitions (with bodies), not declarations. if (f->hasBody()) { Stmt *FuncBody = f->getBody(); // Type name as string QualType QT = f->getReturnType(); std::string TypeStr = QT.getAsString(); // Function name DeclarationName DeclName = f->getNameInfo().getName(); std::string FuncName = DeclName.getAsString(); // Add comment before std::stringstream SSBefore; SSBefore << "// Begin function " << FuncName << " returning " << TypeStr << "\n"; SourceLocation ST = f->getSourceRange().getBegin(); TheRewriter.InsertText(ST, SSBefore.str(), true, true); // And after std::stringstream SSAfter; SSAfter << "\n// End function " << FuncName; ST = FuncBody->getLocEnd().getLocWithOffset(1); TheRewriter.InsertText(ST, SSAfter.str(), true, true); int forCounter=0; Stmt::child_iterator CI, CE = FuncBody->child_end(); for (CI = FuncBody->child_begin(); CI != CE; ++CI) { if (*CI != 0) { if (isa<ForStmt>(*CI)) { forCounter++; std::stringstream MarkerBefore; std::stringstream MarkerAfter; MarkerBefore<<"\nMCPROF_ZONE_ENTER(" << forCounter << ");\n"; MarkerAfter<<"\nMCPROF_ZONE_EXIT(" << forCounter << ");\n"; ForStmt *For = cast<ForStmt>(*CI); SourceLocation ST = For->getLocStart(); TheRewriter.InsertText(ST, MarkerBefore.str(), true, false); Stmt *ForBody = For->getBody(); SourceLocation END = ForBody->getLocEnd(); int offset = Lexer::MeasureTokenLength(END, TheRewriter.getSourceMgr(), TheRewriter.getLangOpts()) + 1; SourceLocation END1 = END.getLocWithOffset(offset); TheRewriter.InsertText(END1, MarkerAfter.str(), true, false); // llvm::errs() << " Detected for loop number " << forCounter // << " in function " << FuncName << "\n"; // InstrumentStmt(ForBody); // Stmt *ForBody = CI->getBody(); // SourceLocation ST = CI->getLocStart(); // char *b = sourceManager->getCharacterData(_b) // llvm::errs() << ST << " is location \n"; } } } } return true; }
Interpreter::CompilationResult Interpreter::EvaluateInternal(const std::string& input, const CompilationOptions& CO, Value* V, /* = 0 */ Transaction** T /* = 0 */) { StateDebuggerRAII stateDebugger(this); // Wrap the expression std::string WrapperName; std::string Wrapper = input; WrapInput(Wrapper, WrapperName); // Disable warnings which doesn't make sense when using the prompt // This gets reset with the clang::Diagnostics().Reset(/*soft*/=false) // using clang's API we simulate: // #pragma warning push // #pragma warning ignore ... // #pragma warning ignore ... // #pragma warning pop SourceLocation Loc = getNextAvailableLoc(); DiagnosticsEngine& Diags = getCI()->getDiagnostics(); Diags.pushMappings(Loc); // The source locations of #pragma warning ignore must be greater than // the ones from #pragma push Diags.setSeverity(clang::diag::warn_unused_expr, clang::diag::Severity::Ignored, SourceLocation()); Diags.setSeverity(clang::diag::warn_unused_call, clang::diag::Severity::Ignored, SourceLocation()); Diags.setSeverity(clang::diag::warn_unused_comparison, clang::diag::Severity::Ignored, SourceLocation()); Diags.setSeverity(clang::diag::ext_return_has_expr, clang::diag::Severity::Ignored, SourceLocation()); if (Transaction* lastT = m_IncrParser->Compile(Wrapper, CO)) { Loc = m_IncrParser->getLastMemoryBufferEndLoc().getLocWithOffset(1); // if the location was the same we are in recursive calls and to avoid an // assert in clang we should increment by a value. if (SourceLocation::getFromRawEncoding(m_LastCustomPragmaDiagPopPoint) == Loc) // Nested #pragma pop-s must be on different source locations. Loc = Loc.getLocWithOffset(1); m_LastCustomPragmaDiagPopPoint = Loc.getRawEncoding(); Diags.popMappings(Loc); assert((lastT->getState() == Transaction::kCommitted || lastT->getState() == Transaction::kRolledBack) && "Not committed?"); if (lastT->getIssuedDiags() != Transaction::kErrors) { Value resultV; if (!V) V = &resultV; if (!lastT->getWrapperFD()) // no wrapper to run return Interpreter::kSuccess; else if (RunFunction(lastT->getWrapperFD(), V) < kExeFirstError){ if (lastT->getCompilationOpts().ValuePrinting != CompilationOptions::VPDisabled && V->isValid() // the !V->needsManagedAllocation() case is handled by // dumpIfNoStorage. && V->needsManagedAllocation()) V->dump(); return Interpreter::kSuccess; } } if (V) *V = Value(); return Interpreter::kFailure; } Diags.popMappings(Loc.getLocWithOffset(1)); return Interpreter::kSuccess; }
void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) { const NamespaceDecl *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace"); const SourceManager &Sources = *Result.SourceManager; if (!locationsInSameFile(Sources, ND->getLocStart(), ND->getRBraceLoc())) return; // Don't require closing comments for namespaces spanning less than certain // number of lines. unsigned StartLine = Sources.getSpellingLineNumber(ND->getLocStart()); unsigned EndLine = Sources.getSpellingLineNumber(ND->getRBraceLoc()); if (EndLine - StartLine + 1 <= ShortNamespaceLines) return; // Find next token after the namespace closing brace. SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1); SourceLocation Loc = AfterRBrace; Token Tok; // Skip whitespace until we find the next token. while (Lexer::getRawToken(Loc, Tok, Sources, Result.Context->getLangOpts())) { Loc = Loc.getLocWithOffset(1); } if (!locationsInSameFile(Sources, ND->getRBraceLoc(), Loc)) return; bool NextTokenIsOnSameLine = Sources.getSpellingLineNumber(Loc) == EndLine; // If we insert a line comment before the token in the same line, we need // to insert a line break. bool NeedLineBreak = NextTokenIsOnSameLine && Tok.isNot(tok::eof); // Try to find existing namespace closing comment on the same line. if (Tok.is(tok::comment) && NextTokenIsOnSameLine) { StringRef Comment(Sources.getCharacterData(Loc), Tok.getLength()); SmallVector<StringRef, 6> Groups; if (NamespaceCommentPattern.match(Comment, &Groups)) { StringRef NamespaceNameInComment = Groups.size() >= 6 ? Groups[5] : ""; // Check if the namespace in the comment is the same. if ((ND->isAnonymousNamespace() && NamespaceNameInComment.empty()) || ND->getNameAsString() == NamespaceNameInComment) { // FIXME: Maybe we need a strict mode, where we always fix namespace // comments with different format. return; } // Otherwise we need to fix the comment. NeedLineBreak = Comment.startswith("/*"); CharSourceRange OldCommentRange = CharSourceRange::getCharRange( SourceRange(Loc, Loc.getLocWithOffset(Tok.getLength()))); diag(Loc, "namespace closing comment refers to a wrong namespace '%0'") << NamespaceNameInComment << FixItHint::CreateReplacement( OldCommentRange, getNamespaceComment(ND, NeedLineBreak)); return; } // This is not a recognized form of a namespace closing comment. // Leave line comment on the same line. Move block comment to the next line, // as it can be multi-line or there may be other tokens behind it. if (Comment.startswith("//")) NeedLineBreak = false; } diag(ND->getLocation(), "namespace not terminated with a closing comment") << FixItHint::CreateInsertion(AfterRBrace, std::string(SpacesBeforeComments, ' ') + getNamespaceComment(ND, NeedLineBreak)); }
bool RewriteUtils::removeParamFromFuncDecl(const ParmVarDecl *PV, unsigned int NumParams, int ParamPos) { SourceRange ParamLocRange = PV->getSourceRange(); int RangeSize; SourceLocation StartLoc = ParamLocRange.getBegin(); if (StartLoc.isInvalid()) { StartLoc = ParamLocRange.getEnd(); RangeSize = PV->getNameAsString().size(); } else { RangeSize = TheRewriter->getRangeSize(ParamLocRange); if (RangeSize == -1) return false; } // The param is the only parameter of the function declaration. // Replace it with void if ((ParamPos == 0) && (NumParams == 1)) { // Note that ')' is included in ParamLocRange for unnamed parameter if (PV->getDeclName()) return !(TheRewriter->ReplaceText(StartLoc, RangeSize, "void")); else return !(TheRewriter->ReplaceText(StartLoc, RangeSize - 1, "void")); } // The param is the last parameter if (ParamPos == static_cast<int>(NumParams - 1)) { int Offset = 0; const char *StartBuf = SrcManager->getCharacterData(StartLoc); TransAssert(StartBuf && "Invalid start buffer!"); while (*StartBuf != ',') { StartBuf--; Offset--; } SourceLocation NewStartLoc = StartLoc.getLocWithOffset(Offset); // Note that ')' is included in ParamLocRange for unnamed parameter // Also note that C++ supports unnamed parameters with default values, // i.e., foo(int x, int = 0); // PV->hasDefaultArg() is to handle this special case if (PV->getDeclName() || PV->hasDefaultArg()) return !(TheRewriter->RemoveText(NewStartLoc, RangeSize - Offset)); else return !(TheRewriter->RemoveText(NewStartLoc, RangeSize - Offset - 1)); } // Clang gives inconsistent RangeSize for named and unnamed parameter decls. // For example, for the first parameter, // foo(int, int); -- RangeSize is 4, i.e., "," is counted // foo(int x, int); -- RangeSize is 5, i.e., ","is not included if (PV->getDeclName()) { // We cannot use the code below: // SourceLocation EndLoc = ParamLocRange.getEnd(); // const char *EndBuf = // ConsumerInstance->SrcManager->getCharacterData(EndLoc); // Because getEnd() returns the start of the last token if this // is a token range. For example, in the above example, // getEnd() points to the start of "x" // See the comments on getRangeSize in clang/lib/Rewriter/Rewriter.cpp int NewRangeSize = 0; const char *StartBuf = SrcManager->getCharacterData(StartLoc); while (NewRangeSize < RangeSize) { StartBuf++; NewRangeSize++; } TransAssert(StartBuf && "Invalid start buffer!"); while (*StartBuf != ',') { StartBuf++; NewRangeSize++; } return !(TheRewriter->RemoveText(StartLoc, NewRangeSize + 1)); } else { return !(TheRewriter->RemoveText(StartLoc, RangeSize)); } }