GCC Code Coverage Report


Directory: ./
File: src/cli/commands/format.cpp
Date: 2023-04-27 00:55:30
Exec Total Coverage
Lines: 27 90 30.0%
Functions: 1 7 14.3%
Branches: 24 188 12.8%

Line Branch Exec Source
1 #include "cli/commands/format.h"
2
3 #include "lexer/buffer.h"
4 #include "lexer/lexer.h"
5 #include "parser/parser.h"
6
7 #include <filesystem>
8 #include <memory>
9
10 namespace fs = std::filesystem;
11
12 namespace lython {
13 argparse::ArgumentParser* FormatCmd::parser() {
14 8 argparse::ArgumentParser* p = new_parser();
15
1/1
✓ Branch 2 taken 8 times.
8 p->add_description("Format lython source files");
16 8 p->add_argument("sources") //
17 8 .remaining() //
18
1/1
✓ Branch 2 taken 8 times.
8 .help("Directories or files to reformat"); //
19
20 8 p->add_argument("--inplace")
21
1/1
✓ Branch 1 taken 8 times.
8 .default_value(false)
22
2/2
✓ Branch 1 taken 8 times.
✓ Branch 4 taken 8 times.
8 .implicit_value(true)
23
1/1
✓ Branch 2 taken 8 times.
8 .help("Reformat files inplace");
24
25 8 p->add_argument("--ast") //
26
1/1
✓ Branch 1 taken 8 times.
8 .default_value(true) //
27
2/2
✓ Branch 1 taken 8 times.
✓ Branch 4 taken 8 times.
8 .implicit_value(true) //
28
1/1
✓ Branch 2 taken 8 times.
8 .help("Use the AST to reformat the code");
29
30 8 p->add_argument("--fuzz") //
31
1/1
✓ Branch 1 taken 8 times.
8 .default_value(true) //
32
2/2
✓ Branch 1 taken 8 times.
✓ Branch 4 taken 8 times.
8 .implicit_value(true) //
33
1/1
✓ Branch 2 taken 8 times.
8 .help("Reads from stdin for fuzzing");
34
35 8 p->add_argument("--extension") //
36
4/6
✓ Branch 1 taken 8 times.
✓ Branch 4 taken 8 times.
✓ Branch 7 taken 8 times.
✓ Branch 8 taken 8 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
24 .default_value(std::vector<std::string>{".ly"}) //
37
1/1
✓ Branch 1 taken 8 times.
8 .nargs(argparse::nargs_pattern::any)
38
1/1
✓ Branch 2 taken 8 times.
8 .help("File extensions require, this is to prevent trying to reformat files that are not "
39 "lython code");
40
41 16 p->add_argument("--dump") //
42
1/1
✓ Branch 1 taken 8 times.
8 .default_value(true) //
43
2/2
✓ Branch 1 taken 8 times.
✓ Branch 4 taken 8 times.
8 .implicit_value(true) //
44
1/1
✓ Branch 2 taken 8 times.
8 .help(
45 "Allways dump parsed AST even if an error occured, (--inplace gets disabled on error)");
46
47 8 return p;
48 16 }
49
50 void find_regular_files(std::string const& path,
51 std::vector<std::string> const& extensions,
52 Array<fs::path>& out);
53 bool has_extension(fs::path const& file, std::vector<std::string> const& extensions);
54 int ast_reformat_file(fs::path const& file, bool _ = false);
55 int tok_reformat_file(fs::path const& file, bool _ = false);
56
57 int FormatCmd::main(argparse::ArgumentParser const& args) {
58 //
59 if (!args.is_used("sources")) {
60 std::cout << "No sources provided" << std::endl;
61 return -1;
62 }
63
64 auto paths = args.get<std::vector<std::string>>("sources");
65
66 Array<fs::path> regular_files;
67 std::vector<std::string> extensions = args.get<std::vector<std::string>>("extension");
68
69 for (std::string const& path: paths) {
70 find_regular_files(path, extensions, regular_files);
71 }
72
73 kwinfo("Found {} files", regular_files.size());
74
75 bool ast = args.get<bool>("--ast");
76 if (args.get<bool>("fuzz")) {
77 if (ast) {
78 return ast_reformat_file("/dev/stdin", args.get<bool>("dump"));
79 } else {
80 return tok_reformat_file("/dev/stdin");
81 }
82 }
83
84 int result = 0;
85
86 for (auto const& path: regular_files) {
87 if (ast) {
88 result += ast_reformat_file(path, args.get<bool>("dump"));
89 } else {
90 result += tok_reformat_file(path);
91 }
92 }
93
94 return result;
95 };
96
97 void find_regular_files(std::string const& path,
98 std::vector<std::string> const& extensions,
99 Array<fs::path>& out) {
100 if (fs::is_regular_file(path)) {
101 out.push_back(path);
102 return;
103 }
104
105 if (fs::is_directory(path)) {
106 for (fs::path const& file: fs::recursive_directory_iterator(path)) {
107 if (fs::is_regular_file(file) && has_extension(file, extensions)) {
108
109 out.push_back(file);
110 }
111 }
112 }
113 }
114
115 bool has_extension(fs::path const& file, std::vector<std::string> const& extensions) {
116
117 for (auto const& ext: extensions) {
118 if (!file.has_extension())
119 return false;
120
121 if (file.extension() == ext) {
122 return true;
123 }
124 }
125
126 return false;
127 }
128
129 int tok_reformat_file(fs::path const& file, bool) {
130 std::cout << "reformat: " << file << std::endl;
131
132 String file_str = file.generic_string().c_str();
133
134 Unique<AbstractBuffer> reader = std::make_unique<FileBuffer>(file_str);
135 Lexer lex(*reader.get());
136
137 StringStream ss;
138 lex.print(ss);
139
140 std::cout << ss.str() << "\n";
141 return 0;
142 }
143
144 int ast_reformat_file(fs::path const& file, bool dump) {
145 std::cout << "reformat: " << file << std::endl;
146
147 String file_str = file.generic_string().c_str();
148
149 Unique<AbstractBuffer> reader = std::make_unique<FileBuffer>(file_str);
150 Lexer lex(*reader.get());
151 Parser parser(lex);
152 Module* mod = nullptr;
153
154 mod = parser.parse_module();
155 parser.show_diagnostics(std::cout);
156 int ec = 0;
157
158 if (parser.has_errors()) {
159 ec = -1;
160 }
161
162 if (parser.has_errors() && !dump) {
163 return ec;
164 }
165
166 if ((parser.has_errors() && dump) || !parser.has_errors()) {
167 StringStream ss;
168 print(str(mod), ss);
169
170 // We should compute a hash of the original file
171 // and a hash of the formated string
172 std::cout << ss.str() << "\n";
173 }
174
175 return ec;
176 }
177
178 } // namespace lython
179