Summary: See http://reviews.llvm.org/D21748 for details.
Reviewers: djasper, klimek
Subscribers: cfe-commits
Differential Revision: https://reviews.llvm.org/D21749
llvm-svn: 277336
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM);
+// FIXME: Remove this function after changing clang-apply-replacements to use
+// Replacements class.
+bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
+ Rewriter &Rewrite);
+
/// \brief Apply all replacements in \c GroupedReplacements.
///
/// \param[in] GroupedReplacements Deduplicated and conflict free Replacements
}
}
+// FIXME: Remove this function after changing clang-apply-replacements to use
+// Replacements class.
+bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
+ Rewriter &Rewrite) {
+ bool Result = true;
+ for (std::vector<tooling::Replacement>::const_iterator I = Replaces.begin(),
+ E = Replaces.end();
+ I != E; ++I) {
+ if (I->isApplicable()) {
+ Result = I->apply(Rewrite) && Result;
+ } else {
+ Result = false;
+ }
+ }
+ return Result;
+}
+
+
+// FIXME: moved from libToolingCore. remove this when std::vector<Replacement>
+// is replaced with tooling::Replacements class.
+static void deduplicate(std::vector<tooling::Replacement> &Replaces,
+ std::vector<tooling::Range> &Conflicts) {
+ if (Replaces.empty())
+ return;
+
+ auto LessNoPath = [](const tooling::Replacement &LHS,
+ const tooling::Replacement &RHS) {
+ if (LHS.getOffset() != RHS.getOffset())
+ return LHS.getOffset() < RHS.getOffset();
+ if (LHS.getLength() != RHS.getLength())
+ return LHS.getLength() < RHS.getLength();
+ return LHS.getReplacementText() < RHS.getReplacementText();
+ };
+
+ auto EqualNoPath = [](const tooling::Replacement &LHS,
+ const tooling::Replacement &RHS) {
+ return LHS.getOffset() == RHS.getOffset() &&
+ LHS.getLength() == RHS.getLength() &&
+ LHS.getReplacementText() == RHS.getReplacementText();
+ };
+
+ // Deduplicate. We don't want to deduplicate based on the path as we assume
+ // that all replacements refer to the same file (or are symlinks).
+ std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
+ Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
+ Replaces.end());
+
+ // Detect conflicts
+ tooling::Range ConflictRange(Replaces.front().getOffset(),
+ Replaces.front().getLength());
+ unsigned ConflictStart = 0;
+ unsigned ConflictLength = 1;
+ for (unsigned i = 1; i < Replaces.size(); ++i) {
+ tooling::Range Current(Replaces[i].getOffset(), Replaces[i].getLength());
+ if (ConflictRange.overlapsWith(Current)) {
+ // Extend conflicted range
+ ConflictRange =
+ tooling::Range(ConflictRange.getOffset(),
+ std::max(ConflictRange.getLength(),
+ Current.getOffset() + Current.getLength() -
+ ConflictRange.getOffset()));
+ ++ConflictLength;
+ } else {
+ if (ConflictLength > 1)
+ Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength));
+ ConflictRange = Current;
+ ConflictStart = i;
+ ConflictLength = 1;
+ }
+ }
+
+ if (ConflictLength > 1)
+ Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength));
+}
+
/// \brief Deduplicates and tests for conflicts among the replacements for each
/// file in \c Replacements. Any conflicts found are reported.
///
assert(Entry != nullptr && "No file entry!");
std::vector<tooling::Range> Conflicts;
- tooling::deduplicate(FileAndReplacements.second, Conflicts);
+ deduplicate(FileAndReplacements.second, Conflicts);
if (Conflicts.empty())
continue;
// However, until we nail down the design of ReplacementGroups, might as well
// leave this as is.
for (const auto &FileAndReplacements : GroupedReplacements) {
- if (!tooling::applyAllReplacements(FileAndReplacements.second, Rewrites))
+ if (!applyAllReplacements(FileAndReplacements.second, Rewrites))
return false;
}
Rewriter &Rewrites, std::string &Result) {
if (Replacements.empty()) return true;
- if (!tooling::applyAllReplacements(Replacements, Rewrites))
+ if (!applyAllReplacements(Replacements, Rewrites))
return false;
SourceManager &SM = Rewrites.getSourceMgr();
class RenamingASTConsumer : public ASTConsumer {
public:
- RenamingASTConsumer(const std::string &NewName, const std::string &PrevName,
- const std::vector<std::string> &USRs,
- tooling::Replacements &Replaces, bool PrintLocations)
- : NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces),
- PrintLocations(PrintLocations) {}
+ RenamingASTConsumer(
+ const std::string &NewName, const std::string &PrevName,
+ const std::vector<std::string> &USRs,
+ std::map<std::string, tooling::Replacements> &FileToReplaces,
+ bool PrintLocations)
+ : NewName(NewName), PrevName(PrevName), USRs(USRs),
+ FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
void HandleTranslationUnit(ASTContext &Context) override {
const auto &SourceMgr = Context.getSourceManager();
<< ":" << FullLoc.getSpellingLineNumber() << ":"
<< FullLoc.getSpellingColumnNumber() << "\n";
}
- Replaces.insert(
- tooling::Replacement(SourceMgr, Loc, PrevNameLen, NewName));
+ // FIXME: better error handling.
+ auto Replace = tooling::Replacement(SourceMgr, Loc, PrevNameLen, NewName);
+ auto Err = FileToReplaces[Replace.getFilePath()].add(Replace);
+ if (Err)
+ llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
+ << llvm::toString(std::move(Err)) << "\n";
}
}
private:
const std::string &NewName, &PrevName;
const std::vector<std::string> &USRs;
- tooling::Replacements &Replaces;
+ std::map<std::string, tooling::Replacements> &FileToReplaces;
bool PrintLocations;
};
std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
return llvm::make_unique<RenamingASTConsumer>(NewName, PrevName, USRs,
- Replaces, PrintLocations);
+ FileToReplaces, PrintLocations);
}
} // namespace rename
public:
RenamingAction(const std::string &NewName, const std::string &PrevName,
const std::vector<std::string> &USRs,
- tooling::Replacements &Replaces, bool PrintLocations = false)
- : NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces),
- PrintLocations(PrintLocations) {
- }
+ std::map<std::string, tooling::Replacements> &FileToReplaces,
+ bool PrintLocations = false)
+ : NewName(NewName), PrevName(PrevName), USRs(USRs),
+ FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
std::unique_ptr<ASTConsumer> newASTConsumer();
private:
const std::string &NewName, &PrevName;
const std::vector<std::string> &USRs;
- tooling::Replacements &Replaces;
+ std::map<std::string, tooling::Replacements> &FileToReplaces;
bool PrintLocations;
};
// Export replacements.
tooling::TranslationUnitReplacements TUR;
- const tooling::Replacements &Replacements = Tool.getReplacements();
- TUR.Replacements.insert(TUR.Replacements.end(), Replacements.begin(),
- Replacements.end());
+ const auto &FileToReplacements = Tool.getReplacements();
+ for (const auto &Entry : FileToReplacements)
+ TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(),
+ Entry.second.end());
yaml::Output YAML(OS);
YAML << TUR;
assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
"Only file locations supported in fix-it hints.");
- Error.Fix.insert(tooling::Replacement(SM, Range, FixIt.CodeToInsert));
+ auto Err =
+ Error.Fix.add(tooling::Replacement(SM, Range, FixIt.CodeToInsert));
+ // FIXME: better error handling.
+ if (Err) {
+ llvm::errs() << "Fix conflicts with existing fix! "
+ << llvm::toString(std::move(Err)) << "\n";
+ }
+ assert(!Err && "Fix conflicts with existing fix!");
}
}
std::string IncludeName =
"#include " + Context.getHeaderInfos().front().Header + "\n";
// Create replacements for the new header.
- clang::tooling::Replacements Insertions = {
- tooling::Replacement(FilePath, UINT_MAX, 0, IncludeName)};
+ clang::tooling::Replacements Insertions;
+ auto Err =
+ Insertions.add(tooling::Replacement(FilePath, UINT_MAX, 0, IncludeName));
+ if (Err)
+ return std::move(Err);
auto CleanReplaces = cleanupAroundReplacements(Code, Insertions, Style);
if (!CleanReplaces)
return CleanReplaces;
+ auto Replaces = std::move(*CleanReplaces);
if (AddQualifiers) {
for (const auto &Info : Context.getQuerySymbolInfos()) {
// Ignore the empty range.
- if (Info.Range.getLength() > 0)
- CleanReplaces->insert({FilePath, Info.Range.getOffset(),
- Info.Range.getLength(),
- Context.getHeaderInfos().front().QualifiedName});
+ if (Info.Range.getLength() > 0) {
+ auto R = tooling::Replacement(
+ {FilePath, Info.Range.getOffset(), Info.Range.getLength(),
+ Context.getHeaderInfos().front().QualifiedName});
+ auto Err = Replaces.add(R);
+ if (Err) {
+ llvm::consumeError(std::move(Err));
+ R = tooling::Replacement(
+ R.getFilePath(), Replaces.getShiftedCodePosition(R.getOffset()),
+ R.getLength(), R.getReplacementText());
+ Replaces = Replaces.merge(tooling::Replacements(R));
+ }
+ }
}
}
- return formatReplacements(Code, *CleanReplaces, Style);
+ return formatReplacements(Code, Replaces, Style);
}
} // namespace include_fixer
}
if (!Quiet)
- errs() << "Added #include " << Context.getHeaderInfos().front().Header
- << '\n';
+ llvm::errs() << "Added #include " << Context.getHeaderInfos().front().Header
+ << "\n";
// Set up a new source manager for applying the resulting replacements.
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions);
namespace {
class ToolTemplateCallback : public MatchFinder::MatchCallback {
- public:
- ToolTemplateCallback(Replacements *Replace) : Replace(Replace) {}
+public:
+ ToolTemplateCallback(std::map<std::string, Replacements> *Replace)
+ : Replace(Replace) {}
void run(const MatchFinder::MatchResult &Result) override {
- // TODO: This routine will get called for each thing that the matchers find.
+ // TODO: This routine will get called for each thing that the matchers
+ // find.
// At this point, you can examine the match, and do whatever you want,
// including replacing the matched text with other text
(void)Replace; // This to prevent an "unused member variable" warning;
}
- private:
- Replacements *Replace;
+private:
+ std::map<std::string, Replacements> *Replace;
};
} // end anonymous namespace
DiagConsumer.finish();
tooling::Replacements Fixes;
for (const ClangTidyError &Error : Context.getErrors())
- Fixes.insert(Error.Fix.begin(), Error.Fix.end());
+ for (const auto &Fix : Error.Fix) {
+ auto Err = Fixes.add(Fix);
+ // FIXME: better error handling. Keep the behavior for now.
+ if (Err) {
+ llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+ return "";
+ }
+ }
if (Errors)
*Errors = Context.getErrors();
auto Result = tooling::applyAllReplacements(Code, Fixes);