Changeset View
Changeset View
Standalone View
Standalone View
src/FixItExporter.cpp
Show All 29 Lines | |||||
30 | #define DEBUG_FIX_IT_EXPORTER | 30 | #define DEBUG_FIX_IT_EXPORTER | ||
31 | 31 | | |||
32 | #ifdef DEBUG_FIX_IT_EXPORTER | 32 | #ifdef DEBUG_FIX_IT_EXPORTER | ||
33 | #include <iostream> | 33 | #include <iostream> | ||
34 | #endif | 34 | #endif | ||
35 | 35 | | |||
36 | using namespace clang; | 36 | using namespace clang; | ||
37 | 37 | | |||
38 | FixItExporter::FixItExporter(DiagnosticsEngine &DiagEngine, SourceManager &SourceMgr, const LangOptions &LangOpts, FixItOptions *FixItOpts) | 38 | FixItExporter::FixItExporter(DiagnosticsEngine &DiagEngine, SourceManager &SourceMgr, const LangOptions &LangOpts, const std::string &exportFixes) | ||
39 | : DiagEngine(DiagEngine), SourceMgr(SourceMgr), LangOpts(LangOpts), | 39 | : DiagEngine(DiagEngine), SourceMgr(SourceMgr), LangOpts(LangOpts), | ||
40 | FixItOpts(FixItOpts) { | 40 | exportFixes(exportFixes) { | ||
41 | Owner = DiagEngine.takeClient(); | 41 | Owner = DiagEngine.takeClient(); | ||
42 | Client = DiagEngine.getClient(); | 42 | Client = DiagEngine.getClient(); | ||
43 | DiagEngine.setClient(this, false); | 43 | DiagEngine.setClient(this, false); | ||
44 | } | 44 | } | ||
45 | 45 | | |||
46 | FixItExporter::~FixItExporter() { | 46 | FixItExporter::~FixItExporter() { | ||
47 | DiagEngine.setClient(Client, Owner.release() != nullptr); | 47 | DiagEngine.setClient(Client, Owner.release() != nullptr); | ||
48 | } | 48 | } | ||
Show All 13 Lines | |||||
62 | 62 | | |||
63 | void FixItExporter::EndSourceFile() { | 63 | void FixItExporter::EndSourceFile() { | ||
64 | if (Client) | 64 | if (Client) | ||
65 | Client->EndSourceFile(); | 65 | Client->EndSourceFile(); | ||
66 | } | 66 | } | ||
67 | 67 | | |||
68 | tooling::Diagnostic FixItExporter::ConvertDiagnostic(const Diagnostic &Info) | 68 | tooling::Diagnostic FixItExporter::ConvertDiagnostic(const Diagnostic &Info) | ||
69 | { | 69 | { | ||
70 | SmallString<100> TmpMessageText; | 70 | SmallString<256> TmpMessageText; | ||
71 | Info.FormatDiagnostic(TmpMessageText); | 71 | Info.FormatDiagnostic(TmpMessageText); | ||
72 | const auto MessageText = TmpMessageText.slice(0, TmpMessageText.find_last_of('[') - 1).str(); | 72 | const auto MessageText = TmpMessageText.slice(0, TmpMessageText.find_last_of('[') - 1).str(); | ||
73 | const auto CheckName = TmpMessageText.slice(TmpMessageText.find_last_of('[') + 3, | 73 | const auto CheckName = TmpMessageText.slice(TmpMessageText.find_last_of('[') + 3, | ||
74 | TmpMessageText.find_last_of(']')).str(); | 74 | TmpMessageText.find_last_of(']')).str(); | ||
75 | llvm::StringRef CurrentBuildDir; // Not needed? | 75 | llvm::StringRef CurrentBuildDir; // Not needed? | ||
76 | 76 | | |||
77 | tooling::Diagnostic ToolingDiag(CheckName, | 77 | tooling::Diagnostic ToolingDiag(CheckName, | ||
78 | tooling::Diagnostic::Warning, | 78 | tooling::Diagnostic::Warning, | ||
79 | CurrentBuildDir); | 79 | CurrentBuildDir); | ||
80 | ToolingDiag.Message = tooling::DiagnosticMessage(MessageText, SourceMgr, Info.getLocation()); | 80 | ToolingDiag.Message = tooling::DiagnosticMessage(MessageText, SourceMgr, Info.getLocation()); | ||
81 | return ToolingDiag; | 81 | return ToolingDiag; | ||
82 | } | 82 | } | ||
83 | 83 | | |||
84 | tooling::Replacement FixItExporter::ConvertFixIt(const FixItHint &Hint) | 84 | tooling::Replacement FixItExporter::ConvertFixIt(const FixItHint &Hint) | ||
85 | { | 85 | { | ||
86 | // TODO: Proper handling of macros | ||||
87 | // https://stackoverflow.com/questions/24062989/clang-fails-replacing-a-statement-if-it-contains-a-macro | ||||
86 | tooling::Replacement Replacement; | 88 | tooling::Replacement Replacement; | ||
87 | if (Hint.CodeToInsert.empty()) { | 89 | if (Hint.CodeToInsert.empty()) { | ||
88 | if (Hint.InsertFromRange.isValid()) { | 90 | if (Hint.InsertFromRange.isValid()) { | ||
89 | clang::SourceLocation b(Hint.InsertFromRange.getBegin()), _e(Hint.InsertFromRange.getEnd()); | 91 | clang::SourceLocation b(Hint.InsertFromRange.getBegin()), _e(Hint.InsertFromRange.getEnd()); | ||
90 | if (b.isMacroID()) | 92 | if (b.isMacroID()) | ||
91 | b = SourceMgr.getSpellingLoc(b); | 93 | b = SourceMgr.getSpellingLoc(b); | ||
92 | if (_e.isMacroID()) | 94 | if (_e.isMacroID()) | ||
93 | _e = SourceMgr.getSpellingLoc(_e); | 95 | _e = SourceMgr.getSpellingLoc(_e); | ||
94 | clang::SourceLocation e(clang::Lexer::getLocForEndOfToken(_e, 0, SourceMgr, LangOpts)); | 96 | clang::SourceLocation e(clang::Lexer::getLocForEndOfToken(_e, 0, SourceMgr, LangOpts)); | ||
95 | StringRef Text(SourceMgr.getCharacterData(b), | 97 | StringRef Text(SourceMgr.getCharacterData(b), | ||
96 | SourceMgr.getCharacterData(e)-SourceMgr.getCharacterData(b)); | 98 | SourceMgr.getCharacterData(e) - SourceMgr.getCharacterData(b)); | ||
97 | return tooling::Replacement(SourceMgr, Hint.RemoveRange, Text); | 99 | return tooling::Replacement(SourceMgr, Hint.RemoveRange, Text); | ||
98 | } | 100 | } | ||
99 | return tooling::Replacement(SourceMgr, Hint.RemoveRange, ""); | 101 | return tooling::Replacement(SourceMgr, Hint.RemoveRange, ""); | ||
100 | } | 102 | } | ||
101 | return tooling::Replacement(SourceMgr, Hint.RemoveRange, Hint.CodeToInsert); | 103 | return tooling::Replacement(SourceMgr, Hint.RemoveRange, Hint.CodeToInsert); | ||
102 | } | 104 | } | ||
103 | 105 | | |||
104 | void FixItExporter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { | 106 | void FixItExporter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { | ||
105 | // Default implementation (Warnings/errors count). | 107 | // Default implementation (Warnings/errors count). | ||
106 | DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); | 108 | DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); | ||
107 | 109 | | |||
108 | // Let origianl client do it's handling | 110 | // Convert and record warning diagnostics | ||
109 | if (Client) | 111 | if (DiagLevel == DiagnosticsEngine::Warning) { | ||
110 | Client->HandleDiagnostic(DiagLevel, Info); | | |||
111 | | ||||
112 | // We only deal with warnings | | |||
113 | if (DiagLevel != DiagnosticsEngine::Warning) | | |||
114 | return; | | |||
115 | | ||||
116 | // Convert and record this diagnostic | | |||
117 | auto ToolingDiag = ConvertDiagnostic(Info); | 112 | auto ToolingDiag = ConvertDiagnostic(Info); | ||
118 | for (unsigned Idx = 0, Last = Info.getNumFixItHints(); | 113 | for (unsigned Idx = 0, Last = Info.getNumFixItHints(); | ||
119 | Idx < Last; ++Idx) { | 114 | Idx < Last; ++Idx) { | ||
120 | const FixItHint &Hint = Info.getFixItHint(Idx); | 115 | const FixItHint &Hint = Info.getFixItHint(Idx); | ||
121 | const auto replacement = ConvertFixIt(Hint); | 116 | const auto replacement = ConvertFixIt(Hint); | ||
122 | #ifdef DEBUG_FIX_IT_EXPORTER | 117 | #ifdef DEBUG_FIX_IT_EXPORTER | ||
123 | const auto FileName = SourceMgr.getFilename(Info.getLocation()); | 118 | const auto FileName = SourceMgr.getFilename(Info.getLocation()); | ||
124 | std::cerr << "Handling Fixit #" << Idx << " for " << FileName.str() << std::endl; | 119 | std::cerr << "Handling Fixit #" << Idx << " for " << FileName.str() << std::endl; | ||
Show All 10 Lines | 128 | #endif | |||
135 | auto error = Replacements.add(ConvertFixIt(Hint)); | 130 | auto error = Replacements.add(ConvertFixIt(Hint)); | ||
136 | if (error) { | 131 | if (error) { | ||
137 | Diag(Info.getLocation(), diag::note_fixit_failed); | 132 | Diag(Info.getLocation(), diag::note_fixit_failed); | ||
138 | } | 133 | } | ||
139 | } | 134 | } | ||
140 | TUDiag.Diagnostics.push_back(ToolingDiag); | 135 | TUDiag.Diagnostics.push_back(ToolingDiag); | ||
141 | } | 136 | } | ||
142 | 137 | | |||
138 | // Let original client do it's handling | ||||
139 | if (Client) | ||||
140 | Client->HandleDiagnostic(DiagLevel, Info); | ||||
141 | } | ||||
142 | | ||||
143 | void FixItExporter::Export() { | 143 | void FixItExporter::Export() { | ||
144 | std::error_code EC; | 144 | std::error_code EC; | ||
145 | llvm::raw_fd_ostream OS(TUDiag.MainSourceFile + ".yaml", EC, llvm::sys::fs::F_None); | 145 | // FIXME: aggregation | ||
146 | llvm::raw_fd_ostream OS(exportFixes, EC, llvm::sys::fs::F_None); | ||||
146 | llvm::yaml::Output YAML(OS); | 147 | llvm::yaml::Output YAML(OS); | ||
147 | YAML << TUDiag; | 148 | YAML << TUDiag; | ||
148 | } | 149 | } | ||
149 | 150 | | |||
150 | void FixItExporter::Diag(SourceLocation Loc, unsigned DiagID) { | 151 | void FixItExporter::Diag(SourceLocation Loc, unsigned DiagID) { | ||
151 | // When producing this diagnostic, we temporarily bypass ourselves, | 152 | // When producing this diagnostic, we temporarily bypass ourselves, | ||
152 | // clear out any current diagnostic, and let the downstream client | 153 | // clear out any current diagnostic, and let the downstream client | ||
153 | // format the diagnostic. | 154 | // format the diagnostic. | ||
154 | DiagEngine.setClient(Client, false); | 155 | DiagEngine.setClient(Client, false); | ||
155 | DiagEngine.Clear(); | 156 | DiagEngine.Clear(); | ||
156 | DiagEngine.Report(Loc, DiagID); | 157 | DiagEngine.Report(Loc, DiagID); | ||
157 | DiagEngine.setClient(this, false); | 158 | DiagEngine.setClient(this, false); | ||
158 | } | 159 | } |