[PATCH] D43764: [clang-apply-replacements] Convert tooling::Replacements to tooling::AtomicChange for conflict resolving of changes, code cleanup, and code formatting.

Eric Liu via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 9 08:47:02 PST 2018


ioeric added inline comments.


================
Comment at: clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp:188
 
-bool mergeAndDeduplicate(const TUReplacements &TUs,
-                         FileToReplacementsMap &GroupedReplacements,
-                         clang::SourceManager &SM) {
-
-  // Group all replacements by target file.
+static llvm::DenseMap<const FileEntry *, std::vector<tooling::Replacement>>
+groupReplacements(const TUReplacements &TUs, const TUDiagnostics &TUDs,
----------------
Add a brief comment describe what this function does, specifically about `TUs` and `TUDs`.


================
Comment at: clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp:190
+groupReplacements(const TUReplacements &TUs, const TUDiagnostics &TUDs,
+                  FileToChangesMap &FileChanges, clang::SourceManager &SM) {
   std::set<StringRef> Warned;
----------------
nit: `SM` can be `const`. And put `SM` (input arg) before `FileChanges` (output arg).


================
Comment at: clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp:213-221
           // Use the file manager to deduplicate paths. FileEntries are
           // automatically canonicalized.
-          if (const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath())) {
+          if (const FileEntry *Entry =
+                  SM.getFileManager().getFile(R.getFilePath())) {
             GroupedReplacements[Entry].push_back(R);
           } else if (Warned.insert(R.getFilePath()).second) {
+            errs() << "Described file '" << R.getFilePath()
----------------
This code block is the same as the one in the above loop. Consider pulling it into a lambda.


================
Comment at: clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp:276-279
+    if (R.getLength() == 0)
+      addInsertion(R);
+    else
+      addReplacement(R);
----------------
`AtomicChange::replace` also handles insertions, so I think there is no need for the distinction here.

(See my comment in where `ReplacementsToAtomicChanges` is used; I think the class might not be needed.)


================
Comment at: clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp:290
+    std::vector<tooling::AtomicChange> Changes;
+    for (const auto &R : AllChanges.getReplacements()) {
+      tooling::AtomicChange Change(Entry->getName(), Entry->getName());
----------------
I might be missing something, but why do we need to pull replacements from the result change into a set of changes? 


================
Comment at: clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp:317
+    if (llvm::Error Error =
+            AllChanges.insert(*SM, BeginLoc.getLocWithOffset(R.getOffset()),
+                              R.getReplacementText())) {
----------------
By default, this inserts R after the existing insertion in the same location (if there is any). But it's impossible for apply-all-replacements to know in which order they should be applied, so I would suggest treating this as conflict. Sorry that I might have confused you by asking to add a test case where two identical insertions are both applied (because they are order *independent*), but `AtomicChange::replace` also supports that and would report error when two insertions are order *dependent*.

`AtomicChange::replace` and `AtomicChange::insert` have almost the same semantic except that you could specify the expected order of insertions on the same location with `insert`, but in our case, we wouldn't know which order is desired.


================
Comment at: clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp:353
+    const FileEntry *Entry = FileAndReplacements.first;
+    ReplacementsToAtomicChanges DeduplicatedChanges(SM, Entry);
+    for (const auto &R : FileAndReplacements.second)
----------------
Sorry that I didn't make myself clear... what you had in the previous revision was correct (for the current use case of apply-all-replacements) i.e. store all replacements in one `AtomicChange`, which allows you to detect conflicts on the fly. So the code here can be simplified as:

```
...
Entry = ...;
AtomicChange FileChange;
for (const auto &R : FileAndReplacements.second) {
  auto Err = FileChange.replace( <args from R> );
  if (Err)
    reportConflict(Entry, std::move(Err));  // reportConflict as we go
}
FileChanges.emplace(Entry, {FileChange});
...
```

I think with this set up, you wouldn't need `ReplacementsToAtomicChanges`, `ConflictError` and `reportConflicts`.


================
Comment at: test/clang-apply-replacements/Inputs/basic/file2.yaml:1
 ---
 MainSourceFile:     source2.cpp
----------------
Could you please add two test cases for insertions: 1) identical insertions (e.g. "AB" and "AB") at the same location are applied sucessfully and 2) order-dependent insertions (e.g. "AB" and "BA") are detected.

(It might make sense to do this in a different file)


================
Comment at: test/clang-apply-replacements/Inputs/conflict/expected.txt:8
+  Replace 9:5-9:11 with "elem"
 The following changes conflict:
   Remove 12:3-12:14
----------------
How could one replacement (`Remove 12:3-12:14`) conflict with itself?


https://reviews.llvm.org/D43764





More information about the cfe-commits mailing list