Coverage Report

Created: 2017-10-03 07:32

/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/clang/lib/Frontend/Rewrite/FixItRewriter.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===//
2
//
3
//                     The LLVM Compiler Infrastructure
4
//
5
// This file is distributed under the University of Illinois Open Source
6
// License. See LICENSE.TXT for details.
7
//
8
//===----------------------------------------------------------------------===//
9
//
10
// This is a diagnostic client adaptor that performs rewrites as
11
// suggested by code modification hints attached to diagnostics. It
12
// then forwards any diagnostics to the adapted diagnostic client.
13
//
14
//===----------------------------------------------------------------------===//
15
16
#include "clang/Rewrite/Frontend/FixItRewriter.h"
17
#include "clang/Basic/FileManager.h"
18
#include "clang/Basic/SourceLocation.h"
19
#include "clang/Basic/SourceManager.h"
20
#include "clang/Edit/Commit.h"
21
#include "clang/Edit/EditsReceiver.h"
22
#include "clang/Frontend/FrontendDiagnostic.h"
23
#include "llvm/Support/Path.h"
24
#include "llvm/Support/raw_ostream.h"
25
#include <cstdio>
26
#include <memory>
27
28
using namespace clang;
29
30
FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
31
                             const LangOptions &LangOpts,
32
                             FixItOptions *FixItOpts)
33
  : Diags(Diags),
34
    Editor(SourceMgr, LangOpts),
35
    Rewrite(SourceMgr, LangOpts),
36
    FixItOpts(FixItOpts),
37
    NumFailures(0),
38
58
    PrevDiagSilenced(false) {
39
58
  Owner = Diags.takeClient();
40
58
  Client = Diags.getClient();
41
58
  Diags.setClient(this, false);
42
58
}
43
44
58
FixItRewriter::~FixItRewriter() {
45
58
  Diags.setClient(Client, Owner.release() != nullptr);
46
58
}
47
48
0
bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) {
49
0
  const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID);
50
0
  if (
!RewriteBuf0
)
return true0
;
51
0
  RewriteBuf->write(OS);
52
0
  OS.flush();
53
0
  return false;
54
0
}
55
56
namespace {
57
58
class RewritesReceiver : public edit::EditsReceiver {
59
  Rewriter &Rewrite;
60
61
public:
62
56
  RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
63
64
271
  void insert(SourceLocation loc, StringRef text) override {
65
271
    Rewrite.InsertText(loc, text);
66
271
  }
67
493
  void replace(CharSourceRange range, StringRef text) override {
68
493
    Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
69
493
  }
70
};
71
72
}
73
74
bool FixItRewriter::WriteFixedFiles(
75
58
            std::vector<std::pair<std::string, std::string> > *RewrittenFiles) {
76
58
  if (
NumFailures > 0 && 58
!FixItOpts->FixWhatYouCan2
) {
77
2
    Diag(FullSourceLoc(), diag::warn_fixit_no_changes);
78
2
    return true;
79
2
  }
80
56
81
56
  RewritesReceiver Rec(Rewrite);
82
56
  Editor.applyRewrites(Rec);
83
56
84
56
  if (
FixItOpts->InPlace56
) {
85
54
    // Overwriting open files on Windows is tricky, but the rewriter can do it
86
54
    // for us.
87
54
    Rewrite.overwriteChangedFiles();
88
54
    return false;
89
54
  }
90
2
91
4
  
for (iterator I = buffer_begin(), E = buffer_end(); 2
I != E4
;
++I2
) {
92
2
    const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first);
93
2
    int fd;
94
2
    std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd);
95
2
    std::error_code EC;
96
2
    std::unique_ptr<llvm::raw_fd_ostream> OS;
97
2
    if (
fd != -12
) {
98
1
      OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true));
99
2
    } else {
100
1
      OS.reset(new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::F_None));
101
1
    }
102
2
    if (
EC2
) {
103
0
      Diags.Report(clang::diag::err_fe_unable_to_open_output) << Filename
104
0
                                                              << EC.message();
105
0
      continue;
106
0
    }
107
2
    RewriteBuffer &RewriteBuf = I->second;
108
2
    RewriteBuf.write(*OS);
109
2
    OS->flush();
110
2
111
2
    if (RewrittenFiles)
112
1
      RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename));
113
2
  }
114
58
115
58
  return false;
116
58
}
117
118
2.13k
bool FixItRewriter::IncludeInDiagnosticCounts() const {
119
2.13k
  return Client ? 
Client->IncludeInDiagnosticCounts()2.13k
:
true0
;
120
2.13k
}
121
122
void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
123
838
                                     const Diagnostic &Info) {
124
838
  // Default implementation (Warnings/errors count).
125
838
  DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
126
838
127
838
  if (!FixItOpts->Silent ||
128
2
      DiagLevel >= DiagnosticsEngine::Error ||
129
0
      
(DiagLevel == DiagnosticsEngine::Note && 0
!PrevDiagSilenced0
) ||
130
838
      
(DiagLevel > DiagnosticsEngine::Note && 0
Info.getNumFixItHints()0
)) {
131
838
    Client->HandleDiagnostic(DiagLevel, Info);
132
838
    PrevDiagSilenced = false;
133
838
  } else {
134
0
    PrevDiagSilenced = true;
135
0
  }
136
838
137
838
  // Skip over any diagnostics that are ignored or notes.
138
838
  if (DiagLevel <= DiagnosticsEngine::Note)
139
125
    return;
140
713
  // Skip over errors if we are only fixing warnings.
141
713
  
if (713
DiagLevel >= DiagnosticsEngine::Error && 713
FixItOpts->FixOnlyWarnings455
) {
142
1
    ++NumFailures;
143
1
    return;
144
1
  }
145
712
146
712
  // Make sure that we can perform all of the modifications we
147
712
  // in this diagnostic.
148
712
  edit::Commit commit(Editor);
149
712
  for (unsigned Idx = 0, Last = Info.getNumFixItHints();
150
1.51k
       
Idx < Last1.51k
;
++Idx802
) {
151
802
    const FixItHint &Hint = Info.getFixItHint(Idx);
152
802
153
802
    if (
Hint.CodeToInsert.empty()802
) {
154
169
      if (Hint.InsertFromRange.isValid())
155
26
        commit.insertFromRange(Hint.RemoveRange.getBegin(),
156
26
                           Hint.InsertFromRange, /*afterToken=*/false,
157
26
                           Hint.BeforePreviousInsertions);
158
169
      else
159
143
        commit.remove(Hint.RemoveRange);
160
802
    } else {
161
633
      if (Hint.RemoveRange.isTokenRange() ||
162
389
          Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
163
362
        commit.replace(Hint.RemoveRange, Hint.CodeToInsert);
164
633
      else
165
271
        commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
166
271
                    /*afterToken=*/false, Hint.BeforePreviousInsertions);
167
633
    }
168
802
  }
169
670
  bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable();
170
712
171
712
  if (
!CanRewrite712
) {
172
42
    if (Info.getNumFixItHints() > 0)
173
0
      Diag(Info.getLocation(), diag::note_fixit_in_macro);
174
42
175
42
    // If this was an error, refuse to perform any rewriting.
176
42
    if (
DiagLevel >= DiagnosticsEngine::Error42
) {
177
2
      if (++NumFailures == 1)
178
1
        Diag(Info.getLocation(), diag::note_fixit_unfixed_error);
179
2
    }
180
42
    return;
181
42
  }
182
670
  
183
670
  
if (670
!Editor.commit(commit)670
) {
184
0
    ++NumFailures;
185
0
    Diag(Info.getLocation(), diag::note_fixit_failed);
186
0
    return;
187
0
  }
188
670
189
670
  Diag(Info.getLocation(), diag::note_fixit_applied);
190
670
}
191
192
/// \brief Emit a diagnostic via the adapted diagnostic client.
193
673
void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) {
194
673
  // When producing this diagnostic, we temporarily bypass ourselves,
195
673
  // clear out any current diagnostic, and let the downstream client
196
673
  // format the diagnostic.
197
673
  Diags.setClient(Client, false);
198
673
  Diags.Clear();
199
673
  Diags.Report(Loc, DiagID);
200
673
  Diags.setClient(this, false);
201
673
}
202
203
58
FixItOptions::~FixItOptions() {}