@@ -120,6 +120,7 @@ private final class InvalidTagChecker extends DocTreePathScanner<Void, Void> {
120
120
private final ImmutableSet <String > parameters ;
121
121
122
122
private final Pattern misplacedCurly ;
123
+ private final Pattern parensRatherThanCurly ;
123
124
124
125
private final Set <DocTree > fixedTags = new HashSet <>();
125
126
@@ -128,14 +129,13 @@ private InvalidTagChecker(
128
129
this .state = state ;
129
130
this .validTags = validTags ;
130
131
this .parameters = parameters ;
131
- this .misplacedCurly =
132
- Pattern .compile (
133
- String .format (
134
- "@(%s)\\ {" ,
135
- validTags .stream ()
136
- .filter (tag -> tag .type () == TagType .INLINE )
137
- .map (JavadocTag ::name )
138
- .collect (joining ("|" ))));
132
+ String validInlineTags =
133
+ validTags .stream ()
134
+ .filter (tag -> tag .type () == TagType .INLINE )
135
+ .map (JavadocTag ::name )
136
+ .collect (joining ("|" ));
137
+ this .misplacedCurly = Pattern .compile (String .format ("@(%s)\\ {" , validInlineTags ));
138
+ this .parensRatherThanCurly = Pattern .compile (String .format ("\\ (@(%s)" , validInlineTags ));
139
139
}
140
140
141
141
@ Override
@@ -163,12 +163,14 @@ public Void visitErroneous(ErroneousTree erroneousTree, Void unused) {
163
163
@ Override
164
164
public Void visitText (TextTree node , Void unused ) {
165
165
handleMalformedTags (node );
166
+ handleIncorrectParens (node );
166
167
handleDanglingParams (node );
167
168
return super .visitText (node , null );
168
169
}
169
170
170
171
private void handleMalformedTags (TextTree node ) {
171
- Matcher matcher = misplacedCurly .matcher (node .getBody ());
172
+ String body = node .getBody ();
173
+ Matcher matcher = misplacedCurly .matcher (body );
172
174
Comment comment = ((DCDocComment ) getCurrentPath ().getDocComment ()).comment ;
173
175
while (matcher .find ()) {
174
176
int beforeAt = comment .getSourcePos (((DCText ) node ).pos + matcher .start ());
@@ -186,6 +188,57 @@ private void handleMalformedTags(TextTree node) {
186
188
}
187
189
}
188
190
191
+ private void handleIncorrectParens (TextTree node ) {
192
+ String body = node .getBody ();
193
+ Matcher matcher = parensRatherThanCurly .matcher (body );
194
+ Comment comment = ((DCDocComment ) getCurrentPath ().getDocComment ()).comment ;
195
+ while (matcher .find ()) {
196
+ int beforeAt = comment .getSourcePos (((DCText ) node ).pos + matcher .start ());
197
+ SuggestedFix .Builder fix = SuggestedFix .builder ().replace (beforeAt , beforeAt + 1 , "{" );
198
+
199
+ Optional <Integer > found = findClosingBrace (body , matcher .start (1 ));
200
+ found .ifPresent (
201
+ pos -> {
202
+ int closing = comment .getSourcePos (((DCText ) node ).pos + pos );
203
+ fix .replace (closing , closing + 1 , "}" );
204
+ });
205
+
206
+ state .reportMatch (
207
+ buildDescription (
208
+ getDiagnosticPosition (beforeAt , getCurrentPath ().getTreePath ().getLeaf ()))
209
+ .setMessage (
210
+ String .format (
211
+ "Curly braces should be used for inline Javadoc tags: {%s ...}" ,
212
+ matcher .group (1 )))
213
+ .addFix (fix .build ())
214
+ .build ());
215
+ }
216
+ }
217
+
218
+ /** Looks for a matching closing brace, if one is found. */
219
+ private Optional <Integer > findClosingBrace (String body , int startPos ) {
220
+ int parenDepth = 0 ;
221
+ for (int pos = startPos ; pos < body .length (); ++pos ) {
222
+ char c = body .charAt (pos );
223
+ switch (c ) {
224
+ case '(' :
225
+ parenDepth ++;
226
+ continue ;
227
+ case ')' :
228
+ if (parenDepth == 0 ) {
229
+ return Optional .of (pos );
230
+ }
231
+ parenDepth --;
232
+ break ;
233
+ case '}' :
234
+ return Optional .empty ();
235
+ default :
236
+ // fall out
237
+ }
238
+ }
239
+ return Optional .empty ();
240
+ }
241
+
189
242
private void handleDanglingParams (TextTree node ) {
190
243
Matcher matcher = PARAM_MATCHER .matcher (node .getBody ());
191
244
Comment comment = ((DCDocComment ) getCurrentPath ().getDocComment ()).comment ;
0 commit comments