1# Copyright 2020 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15# Simple libcore patch-style checking based on http://go/libcore-patch-style.
16#
17# See sample-input.java for examples of matches and failures.
18#
19# Usage:
20#  awk -f libcore-patch-style.awk <file0> [... fileN]
21#
22# This script ignores any files whose name does not end in the suffix ".java".
23#
24# To scan all source code in the libcore tree:
25#  $ cd libcore
26#  $ find . -type f | xargs awk -f tools/patch-style/libcore-patch-style.awk
27#
28# To find sources with the most issues:
29#  $ cd libcore
30#  $ find . -type f | xargs awk -f tools/patch-style/libcore-patch-style.awk \
31#                   | grep -F ./ | sed -e 's/:.*//' | uniq -c | sort -n -r | head
32
33BEGIN {
34  g_errors = 0                    # Number of errors accumulated.
35  g_expected_end = ""             # Expected END line.
36  g_max_length = 100              # Maximum line length for markers (0 == no checking).
37  g_stop_oneline_interleaving = 0 # Error one-line comments between BEGIN and END markers.
38}
39
40BEGINFILE {
41  # Skip files whose names do not have a .java suffix.
42  if (FILENAME !~ /\.java$/) {
43    nextfile
44  }
45
46  # Reset the line number for reporting errors back to zero.
47  NR = 0
48
49  # Clear expected end marker as processing a new file.
50  g_expected_end = ""
51}
52
53function error(message) {
54  print(FILENAME ":" NR ":", message "\n")
55  g_errors += 1;
56}
57
58function expectationError(reason, expected, actual) {
59  error(reason "\n  Expected: \"" expected "\"\n  Actual:   \"" actual "\"")
60}
61
62function inputError(reason, actual) {
63  error(reason "\n  Input:    \"" actual "\"")
64}
65
66function checkLineLength(line) {
67  if (g_max_length > 0 && length(line) > g_max_length) {
68    inputError("Line too long", line)
69  }
70}
71
72function leftTrim(message) {
73  return gensub(/^ */, "", 1, message)
74}
75
76function failIfEndExpected() {
77  if (g_expected_end != "") {
78    expectationError("Missing END marker.", g_expected_end, $0)
79    g_expected_end = ""
80  }
81}
82
83function expectEndFor(begin_line) {
84  g_expected_end = begin_line
85  sub("BEGIN", "END", g_expected_end)
86}
87
88function actualEndFor(actual_line) {
89  if (actual_line != g_expected_end) {
90    expectationError("Bad END marker.", g_expected_end, actual_line)
91  }
92  g_expected_end = ""
93}
94
95function processBeginMarker(line) {
96  failIfEndExpected()
97  expectEndFor(line)
98}
99
100function processEndMarker(line) {
101  actualEndFor(line)
102}
103
104# BEGIN marker ending in a period.
105/^ *\/\/ BEGIN Android-(added|changed|note|removed):.*\./ {
106  checkLineLength($0)
107  processBeginMarker($0)
108  next
109}
110
111# BEGIN marker ending in a period.
112/^ *\/\/ END Android-(added|changed|note|removed):.*\./ {
113  checkLineLength($0)
114  processEndMarker($0)
115  next
116}
117
118# BEGIN marker ending in a bug reference.
119/^ *\/\/ BEGIN Android-(added|changed|note|removed):.*[.](http:\/\/)?b\/[1-9][0-9]*/ {
120  checkLineLength($0)
121  processBeginMarker($0)
122  next
123}
124
125# BEGIN marker ending in anything else, oops!
126/^ *\/\/ BEGIN Android-(added|changed|note|removed)[^:].*/ {
127  inputError("BEGIN marker is missing colon or description.", $0)
128  next
129}
130
131# END marker, should be paired with last BEGIN marker.
132/^ *\/\/ END Android-(added|changed|note|removed):.*/ {
133  checkLineLength($0)
134  processEndMarker($0)
135  next
136}
137
138# One line change marker ending in a period.
139/^ *\/\/ Android-(added|changed|note|removed):.*[.]/ {
140  checkLineLength($0)
141  if (g_stop_oneline_interleaving) {
142    failIfEndExpected()
143  }
144  next
145}
146
147# One line change marker ending in a bug reference.
148/^ *\/\/ Android-(added|changed|note|removed):[.](http:\/\/)?b\/[1-9][0-9]*/ {
149  checkLineLength($0)
150  if (g_stop_oneline_interleaving) {
151    failIfEndExpected()
152  }
153  next
154}
155
156# One line change marker missing comment after colon.
157/^ *\/\/ BEGIN Android-(added|changed|note|removed).*/ {
158  inputError("Bad change marker: missing colon or description.", $0)
159  next
160}
161
162# Something that looks like a potential change marker.
163/^ *(\/\*|\/\/|\*) *(Android|ANDROID)-/ {
164  if (g_stop_oneline_interleaving) {
165    failIfEndExpected()
166  }
167  inputError("Bad change marker.", $0)
168  next
169}
170
171END {
172  failIfEndExpected()
173  printf("Found " g_errors " libcore patch style issues.\n")
174  exit g_errors == 0
175}
176