-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathformat.hpp
124 lines (105 loc) · 5.77 KB
/
format.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//
// Created by xflajs00 on 14.03.2023.
//
#ifndef SPECULO_FORMAT_H
#define SPECULO_FORMAT_H
#include "speculo_gen/wrap/SilenceWarningsStart.hpp"
#include <clang/Basic/Diagnostic.h>
#include <clang/Basic/DiagnosticOptions.h>
#include <clang/Basic/FileManager.h>
#include <clang/Basic/SourceManager.h>
#include <clang/Basic/Version.h>
#include <clang/Format/Format.h>
#include <clang/Frontend/TextDiagnosticPrinter.h>
#include <clang/Rewrite/Core/Rewriter.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/InitLLVM.h>
#include <llvm/Support/Process.h>
#include "speculo_gen/wrap/SilenceWarningsEnd.hpp"
inline const char *getInValidBOM(llvm::StringRef BufStr) {
// Check to see if the buffer has a UTF Byte Order Mark (BOM).
// We only support UTF-8 with and without a BOM right now. See
// https://en.wikipedia.org/wiki/Byte_order_mark#Byte_order_marks_by_encoding
// for more information.
const char *InvalidBOM = llvm::StringSwitch<const char *>(BufStr)
.StartsWith(llvm::StringLiteral::withInnerNUL("\x00\x00\xFE\xFF"), "UTF-32 (BE)")
.StartsWith(llvm::StringLiteral::withInnerNUL("\xFF\xFE\x00\x00"), "UTF-32 (LE)")
.StartsWith("\xFE\xFF", "UTF-16 (BE)")
.StartsWith("\xFF\xFE", "UTF-16 (LE)")
.StartsWith("\x2B\x2F\x76", "UTF-7")
.StartsWith("\xF7\x64\x4C", "UTF-1")
.StartsWith("\xDD\x73\x66\x73", "UTF-EBCDIC")
.StartsWith("\x0E\xFE\xFF", "SCSU")
.StartsWith("\xFB\xEE\x28", "BOCU-1")
.StartsWith("\x84\x31\x95\x33", "GB-18030")
.Default(nullptr);
return InvalidBOM;
}
inline clang::FileID createInMemoryFile(llvm::StringRef FileName, llvm::MemoryBuffer *Source, clang::SourceManager &Sources,
clang::FileManager &Files, llvm::vfs::InMemoryFileSystem *MemFS) {
MemFS->addFileNoOwn(FileName, 0, *Source);
auto File = Files.getFile(FileName);
return Sources.createFileID(File ? *File : nullptr, clang::SourceLocation(), clang::SrcMgr::C_User);
}
inline bool fillRanges(llvm::MemoryBuffer *Code, std::vector<clang::tooling::Range> &Ranges) {
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem);
clang::FileManager Files(clang::FileSystemOptions(), InMemoryFileSystem);
clang::DiagnosticsEngine Diagnostics(llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs),
new clang::DiagnosticOptions);
clang::SourceManager Sources(Diagnostics, Files);
clang::FileID ID = createInMemoryFile("<irrelevant>", Code, Sources, Files, InMemoryFileSystem.get());
auto End = Sources.getLocForEndOfFile(ID);
unsigned Length = Sources.getFileOffset(End);
Ranges.emplace_back(0, Length);
return false;
}
inline bool format(llvm::StringRef FileName) {
// On Windows, overwriting a file with an open file mapping doesn't work,
// so read the whole file into memory when formatting in-place.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> CodeOrErr = llvm::MemoryBuffer::getFileAsStream(FileName);
if (std::error_code EC = CodeOrErr.getError()) {
llvm::errs() << EC.message() << "\n";
return true;
}
std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
if (Code->getBufferSize() == 0) return false;// Empty files are formatted correctly.
llvm::StringRef BufStr = Code->getBuffer();
const char *InvalidBOM = getInValidBOM(BufStr);
if (InvalidBOM) {
llvm::errs() << "error: encoding with unsupported byte order mark \"" << InvalidBOM << "\" detected";
if (FileName != "-") llvm::errs() << " in file '" << FileName << "'";
llvm::errs() << ".\n";
return true;
}
std::vector<clang::tooling::Range> Ranges;
if (fillRanges(Code.get(), Ranges)) return true;
llvm::StringRef AssumedFileName = FileName;
llvm::Expected<clang::format::FormatStyle> FormatStyle = clang::format::getLLVMStyle();
if (!FormatStyle) {
llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
return true;
}
FormatStyle->ColumnLimit = 120;
unsigned CursorPosition = 0;
clang::tooling::Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges, AssumedFileName, &CursorPosition);
auto ChangedCode = clang::tooling::applyAllReplacements(Code->getBuffer(), Replaces);
if (!ChangedCode) {
llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
return true;
}
// Get new affected ranges after sorting `#includes`.
Ranges = clang::tooling::calculateRangesAfterReplacements(Replaces, Ranges);
clang::format::FormattingAttemptStatus Status;
clang::tooling::Replacements FormatChanges = reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
Replaces = Replaces.merge(FormatChanges);
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem);
clang::FileManager Files(clang::FileSystemOptions(), InMemoryFileSystem);
clang::DiagnosticsEngine Diagnostics(clang::IntrusiveRefCntPtr<clang::DiagnosticIDs>(new clang::DiagnosticIDs), new clang::DiagnosticOptions);
clang::SourceManager Sources(Diagnostics, Files);
clang::Rewriter Rewrite(Sources, clang::LangOptions());
clang::tooling::applyAllReplacements(Replaces, Rewrite);
if (Rewrite.overwriteChangedFiles()) return true;
return false;
}
#endif//SPECULO_FORMAT_H