1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <hidl-util/FQName.h>
18 #include <hidl-util/Formatter.h>
19 #include <json/json.h>
20 
21 #include <algorithm>
22 #include <iostream>
23 #include <vector>
24 
25 #include "AST.h"
26 #include "Coordinator.h"
27 #include "Lint.h"
28 #include "LintRegistry.h"
29 #include "Location.h"
30 
31 using namespace android;
32 
usage(const char * me)33 static void usage(const char* me) {
34     Formatter out(stderr);
35 
36     out << "Usage: " << me << " [-j] ";
37     Coordinator::emitOptionsUsageString(out);
38     out << " FQNAME...\n\n";
39 
40     out << "Process FQNAME, PACKAGE(.SUBPACKAGE)*@[0-9]+.[0-9]+(::TYPE)?, and provide lints.\n\n";
41 
42     out.indent();
43     out.indent();
44 
45     out << "-h: Prints this menu.\n";
46     out << "-e: The script only errors if FQNAME does not compile (don't error on lints).\n";
47     out << "-j: Prints output in JSON.\n";
48     out.indent([&] {
49         out << "{\n";
50         out.indent([&] {
51             out << "\"level\": \"warning\" | \"error\",\n";
52             out << "\"message\": string,\n";
53             out << "\"filename\": string,\n";
54 
55             out << "\"begin\": { \"line\" : number, \"column\" : number }\n";
56             out << "\"end\": { \"line\" : number, \"column\" : number }\n";
57         });
58         out << "}\n\n";
59     });
60     Coordinator::emitOptionsDetailString(out);
61 
62     out.unindent();
63     out.unindent();
64 }
65 
main(int argc,char ** argv)66 int main(int argc, char** argv) {
67     const char* me = argv[0];
68     if (argc == 1) {
69         usage(me);
70         std::cerr << "ERROR: no fqname specified." << std::endl;
71         exit(1);
72     }
73 
74     bool machineReadable = false;
75     bool errorOnLints = true;
76 
77     Coordinator coordinator;
78     coordinator.parseOptions(argc, argv, "hje", [&](int res, char* /* arg */) {
79         switch (res) {
80             case 'j':
81                 machineReadable = true;
82                 break;
83             case 'e':
84                 errorOnLints = false;
85                 break;
86             case 'h':
87             case '?':
88             default: {
89                 usage(me);
90                 exit(1);
91                 break;
92             }
93         }
94     });
95 
96     argc -= optind;
97     argv += optind;
98 
99     if (argc == 0) {
100         usage(me);
101         std::cerr << "ERROR: no fqname specified." << std::endl;
102         exit(1);
103     }
104 
105     bool haveLints = false;
106     Json::Value lintJsonArray(Json::arrayValue);
107     for (int i = 0; i < argc; ++i) {
108         const char* arg = argv[i];
109 
110         FQName fqName;
111         if (!FQName::parse(arg, &fqName)) {
112             std::cerr << "ERROR: Invalid fully-qualified name as argument: " << arg << "."
113                       << std::endl;
114             exit(1);
115         }
116 
117         std::vector<FQName> targets;
118         if (fqName.isFullyQualified()) {
119             targets.push_back(fqName);
120         } else {
121             status_t err = coordinator.appendPackageInterfacesToVector(fqName, &targets);
122             if (err != OK) {
123                 std::cerr << "ERROR: Could not get sources for: " << arg << "." << std::endl;
124                 exit(1);
125             }
126         }
127 
128         std::vector<Lint> lints;
129         for (const FQName& target : targets) {
130             AST* ast = coordinator.parse(target);
131             if (ast == nullptr) {
132                 std::cerr << "ERROR: Could not parse " << target.name() << ". Aborting."
133                           << std::endl;
134                 exit(1);
135             }
136 
137             LintRegistry::get()->runAllLintFunctions(*ast, &lints);
138         }
139 
140         haveLints = haveLints || !lints.empty();
141 
142         std::sort(lints.begin(), lints.end());
143         if (machineReadable) {
144             for (const Lint& lint : lints) {
145                 lintJsonArray.append(lint.asJson());
146             }
147         } else {
148             if (!lints.empty()) {
149                 std::cout << "Lints for: " << fqName.string() << std::endl << std::endl;
150             }
151 
152             for (const Lint& lint : lints) {
153                 std::cout << lint;
154             }
155         }
156     }
157 
158     if (machineReadable) {
159         Json::StreamWriterBuilder factory;
160         std::unique_ptr<Json::StreamWriter> const writer(factory.newStreamWriter());
161         writer->write(lintJsonArray, &std::cout);
162     }
163 
164     return errorOnLints && haveLints;
165 }
166