blob: 36f37e8b915c76098ac9dc8a638df31e2ede5fee [file] [log] [blame]
Jooyung Hand4fe00e2021-01-11 16:21:53 +09001/*
2 * Copyright (C) 2021, 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#include "comments.h"
17
18#include <android-base/result.h>
19#include <android-base/strings.h>
20
21#include <optional>
Jooyung Han24effbf2021-01-16 10:24:03 +090022#include <regex>
Jooyung Hand4fe00e2021-01-11 16:21:53 +090023#include <string>
24#include <vector>
25
26#include "logging.h"
27
28using android::base::EndsWith;
29using android::base::Error;
30using android::base::Join;
31using android::base::Result;
32using android::base::Split;
33using android::base::StartsWith;
34using android::base::Trim;
35
36namespace android {
37namespace aidl {
38
39namespace {
40
41static const std::string_view kLineCommentBegin = "//";
42static const std::string_view kBlockCommentBegin = "/*";
43static const std::string_view kBlockCommentEnd = "*/";
Jooyung Han24effbf2021-01-16 10:24:03 +090044static const std::string kTagDeprecated = "@deprecated";
45static const std::regex kTagHideRegex{"@hide\\b"};
Jooyung Hand4fe00e2021-01-11 16:21:53 +090046
47std::string ConsumePrefix(const std::string& s, std::string_view prefix) {
48 AIDL_FATAL_IF(!StartsWith(s, prefix), AIDL_LOCATION_HERE);
49 return s.substr(prefix.size());
50}
51
52std::string ConsumeSuffix(const std::string& s, std::string_view suffix) {
53 AIDL_FATAL_IF(!EndsWith(s, suffix), AIDL_LOCATION_HERE);
54 return s.substr(0, s.size() - suffix.size());
55}
56
57struct BlockTag {
58 std::string name;
59 std::string description;
60};
61
Jooyung Han8451a202021-01-16 03:07:06 +090062// Removes comment markers: //, /*, */, optional leading "*" in block comments
Jooyung Hand4fe00e2021-01-11 16:21:53 +090063// - keeps leading spaces, but trims trailing spaces
64// - keeps empty lines
Jooyung Han8451a202021-01-16 03:07:06 +090065std::vector<std::string> TrimmedLines(const Comment& c) {
66 if (c.type == Comment::Type::LINE) return std::vector{ConsumePrefix(c.body, kLineCommentBegin)};
Jooyung Hand4fe00e2021-01-11 16:21:53 +090067
Jooyung Han8451a202021-01-16 03:07:06 +090068 std::string stripped = ConsumeSuffix(ConsumePrefix(c.body, kBlockCommentBegin), kBlockCommentEnd);
Jooyung Hand4fe00e2021-01-11 16:21:53 +090069
70 std::vector<std::string> lines;
71 bool found_first_line = false;
72
73 for (auto& line : Split(stripped, "\n")) {
74 // Delete prefixes like " * ", " *", or " ".
75 size_t idx = 0;
76 for (; idx < line.size() && isspace(line[idx]); idx++)
77 ;
78 if (idx < line.size() && line[idx] == '*') idx++;
79 if (idx < line.size() && line[idx] == ' ') idx++;
80
81 const std::string& sanitized_line = line.substr(idx);
82 size_t i = sanitized_line.size();
83 for (; i > 0 && isspace(sanitized_line[i - 1]); i--)
84 ;
85
86 // Either the size is 0 or everything was whitespace.
87 bool is_empty_line = i == 0;
88
89 found_first_line = found_first_line || !is_empty_line;
90 if (!found_first_line) continue;
91
92 // if is_empty_line, i == 0 so substr == ""
93 lines.push_back(sanitized_line.substr(0, i));
94 }
95 // remove trailing empty lines
96 while (!lines.empty() && Trim(lines.back()).empty()) {
97 lines.pop_back();
98 }
99 return lines;
100}
101
Jooyung Han8451a202021-01-16 03:07:06 +0900102std::vector<BlockTag> BlockTags(const Comment& c) {
Jooyung Hand4fe00e2021-01-11 16:21:53 +0900103 std::vector<BlockTag> tags;
104
105 // current tag and paragraph
106 std::string tag;
107 std::vector<std::string> paragraph;
108
109 auto end_paragraph = [&]() {
110 if (tag.empty()) {
111 paragraph.clear();
112 return;
113 }
114 // paragraph lines are trimed at both ends
115 tags.push_back({tag, Join(paragraph, " ")});
116 tag.clear();
117 paragraph.clear();
118 };
119
Jooyung Han8451a202021-01-16 03:07:06 +0900120 for (const auto& line : TrimmedLines(c)) {
Jooyung Hand4fe00e2021-01-11 16:21:53 +0900121 size_t idx = 0;
122 // skip leading spaces
123 for (; idx < line.size() && isspace(line[idx]); idx++)
124 ;
125
126 if (idx == line.size()) {
127 // skip empty lines
128 } else if (line[idx] == '@') {
129 // end the current paragraph before reading a new block tag (+ description paragraph)
130 end_paragraph();
131
132 size_t end_idx = idx + 1;
133 for (; end_idx < line.size() && isalpha(line[end_idx]); end_idx++)
134 ;
135
136 tag = line.substr(idx, end_idx - idx);
137
138 if (end_idx < line.size() && line[end_idx] == ' ') end_idx++;
139 // skip empty line
140 if (end_idx < line.size()) {
141 paragraph.push_back(line.substr(end_idx));
142 }
143 } else {
144 // gather paragraph lines with leading spaces trimmed
145 paragraph.push_back(line.substr(idx));
146 }
147 }
148
149 end_paragraph();
150
151 return tags;
152}
Jooyung Hand4fe00e2021-01-11 16:21:53 +0900153}
154
Jooyung Han8451a202021-01-16 03:07:06 +0900155bool HasHideInComments(const Comments& comments) {
156 for (const auto& c : comments) {
Jooyung Han24effbf2021-01-16 10:24:03 +0900157 if (c.type == Comment::Type::LINE) continue;
158 if (std::regex_search(c.body, kTagHideRegex)) {
159 return true;
160 }
161 }
162 return false;
163}
164
Jooyung Hand4fe00e2021-01-11 16:21:53 +0900165// Finds @deprecated tag and returns it with optional note which follows the tag.
Jooyung Han8451a202021-01-16 03:07:06 +0900166std::optional<Deprecated> FindDeprecated(const Comments& comments) {
167 for (const auto& c : comments) {
Jooyung Han24effbf2021-01-16 10:24:03 +0900168 if (c.type == Comment::Type::LINE) continue;
Jooyung Han8451a202021-01-16 03:07:06 +0900169 for (const auto& [name, description] : BlockTags(c)) {
Jooyung Hand4fe00e2021-01-11 16:21:53 +0900170 // take the first @deprecated
171 if (kTagDeprecated == name) {
172 return Deprecated{description};
173 }
174 }
175 }
176 return std::nullopt;
177}
178
179} // namespace aidl
180} // namespace android