get rid of intellij.build.toolbox.litegen parameter and use BuildOptions.TOOLBOX_LITE...
[idea/community.git] / java / java-impl / src / com / intellij / codeInspection / java18api / Java8MapApiInspection.java
1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.codeInspection.java18api;
3
4 import com.intellij.codeInsight.Nullability;
5 import com.intellij.codeInsight.PsiEquivalenceUtil;
6 import com.intellij.codeInsight.daemon.QuickFixBundle;
7 import com.intellij.codeInspection.*;
8 import com.intellij.codeInspection.dataFlow.NullabilityUtil;
9 import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
10 import com.intellij.codeInspection.util.LambdaGenerationUtil;
11 import com.intellij.openapi.diagnostic.Logger;
12 import com.intellij.openapi.project.Project;
13 import com.intellij.openapi.util.text.StringUtil;
14 import com.intellij.pom.java.JavaFeature;
15 import com.intellij.psi.*;
16 import com.intellij.psi.codeStyle.CodeStyleManager;
17 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
18 import com.intellij.psi.codeStyle.VariableKind;
19 import com.intellij.psi.search.searches.ReferencesSearch;
20 import com.intellij.psi.util.PsiTreeUtil;
21 import com.intellij.util.ObjectUtils;
22 import com.siyeh.ig.callMatcher.CallMatcher;
23 import com.siyeh.ig.psiutils.*;
24 import one.util.streamex.StreamEx;
25 import org.jetbrains.annotations.Nls;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28
29 import javax.swing.*;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.List;
33
34 import static com.siyeh.ig.psiutils.Java8MigrationUtils.*;
35 import static com.siyeh.ig.psiutils.Java8MigrationUtils.MapCheckCondition.fromConditional;
36
37 public class Java8MapApiInspection extends AbstractBaseJavaLocalInspectionTool {
38   private static final Logger LOG = Logger.getInstance(Java8MapApiInspection.class);
39   public static final String SHORT_NAME = "Java8MapApi";
40   private static final CallMatcher KEY_VALUE_GET_METHODS =
41     CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_MAP_ENTRY, "getKey", "getValue").parameterCount(0);
42
43   @SuppressWarnings("PublicField")
44   public boolean mySuggestMapGetOrDefault = true;
45   @SuppressWarnings("PublicField")
46   public boolean mySuggestMapComputeIfAbsent = true;
47   @SuppressWarnings("PublicField")
48   public boolean mySuggestMapPutIfAbsent = true;
49   @SuppressWarnings("PublicField")
50   public boolean mySuggestMapMerge = true;
51   @SuppressWarnings("PublicField")
52   public boolean mySuggestMapReplaceAll = true;
53   @SuppressWarnings("PublicField")
54   public boolean myTreatGetNullAsContainsKey = false;
55   @SuppressWarnings("PublicField")
56   public boolean mySideEffects = false;
57
58   @Nullable
59   @Override
60   public JComponent createOptionsPanel() {
61     MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this);
62     panel.addCheckbox("Suggest conversion to Map.computeIfAbsent", "mySuggestMapComputeIfAbsent");
63     panel.addCheckbox("Suggest conversion to Map.getOrDefault", "mySuggestMapGetOrDefault");
64     panel.addCheckbox("Suggest conversion to Map.putIfAbsent", "mySuggestMapPutIfAbsent");
65     panel.addCheckbox("Suggest conversion to Map.merge", "mySuggestMapMerge");
66     panel.addCheckbox("Suggest conversion to Map.replaceAll", "mySuggestMapReplaceAll");
67     panel.addCheckbox("Treat 'get(k) != null' the same as 'containsKey(k)' (may change semantics)", "myTreatGetNullAsContainsKey");
68     panel.addCheckbox("Suggest replacement even if lambda may have side effects", "mySideEffects");
69     return panel;
70   }
71
72   @NotNull
73   @Override
74   public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
75     if (!JavaFeature.ADVANCED_COLLECTIONS_API.isFeatureSupported(holder.getFile())) {
76       return PsiElementVisitor.EMPTY_VISITOR;
77     }
78     return new JavaElementVisitor() {
79       @Override
80       public void visitConditionalExpression(PsiConditionalExpression expression) {
81         MapCheckCondition condition = fromConditional(expression, myTreatGetNullAsContainsKey);
82         if(condition == null || condition.hasVariable()) return;
83         PsiExpression existsBranch = condition.getExistsBranch(expression.getThenExpression(), expression.getElseExpression());
84         PsiExpression noneBranch = condition.getNoneBranch(expression.getThenExpression(), expression.getElseExpression());
85         processGetPut(condition, existsBranch, existsBranch, noneBranch);
86       }
87
88       @Override
89       public void visitIfStatement(PsiIfStatement statement) {
90         MapCheckCondition condition = fromConditional(statement, myTreatGetNullAsContainsKey);
91         if(condition == null) return;
92         PsiStatement existsBranch = ControlFlowUtils.stripBraces(condition.getExistsBranch(statement.getThenBranch(), statement.getElseBranch()));
93         PsiStatement noneBranch = ControlFlowUtils.stripBraces(condition.getNoneBranch(statement.getThenBranch(), statement.getElseBranch()));
94         if(existsBranch == null) {
95           processSingleBranch(condition, noneBranch);
96         } else {
97           if(mySuggestMapMerge && condition.isGetNull()) {
98             processMerge(condition, existsBranch, noneBranch);
99           }
100           if(condition.hasVariable()) return;
101           EquivalenceChecker.Match match = EquivalenceChecker.getCanonicalPsiEquivalence().statementsMatch(noneBranch, existsBranch);
102
103           processGetPut(condition, existsBranch, match.getRightDiff(), match.getLeftDiff());
104         }
105       }
106
107       @Override
108       public void visitForeachStatement(PsiForeachStatement statement) {
109         if (!mySuggestMapReplaceAll) return;
110         MapLoopCondition condition = MapLoopCondition.create(statement);
111         if (condition == null) return;
112         PsiMethodCallExpression putCall = condition.extractPut(statement);
113         if (putCall == null) return;
114         PsiExpression[] args = putCall.getArgumentList().getExpressions();
115         if (args.length != 2) return;
116         PsiExpression key = args[0];
117         if (!condition.isKeyAccess(key)) return;
118         PsiExpression value = args[1];
119         if (condition.isEntrySet() && isUsedAsReference(value, condition)) return;
120         if (hasMapUsages(condition, value)) return;
121         if (!LambdaGenerationUtil.canBeUncheckedLambda(value, variable -> condition.getMap().equals(variable))) return;
122
123         ReplaceWithSingleMapOperation fix = ReplaceWithSingleMapOperation.create("replaceAll", putCall, value);
124         holder.registerProblem(statement.getFirstChild(),
125                                QuickFixBundle.message("java.8.map.api.inspection.description", fix.myMethodName), fix);
126       }
127
128       private boolean hasMapUsages(@NotNull MapLoopCondition condition, @Nullable PsiExpression value) {
129         return !VariableAccessUtils.getVariableReferences(condition.getMap(), value).stream()
130           .map(ExpressionUtils::getCallForQualifier)
131           .allMatch(call -> condition.isValueAccess(call));
132       }
133
134       private boolean isUsedAsReference(@NotNull PsiElement value, @NotNull MapLoopCondition condition) {
135         return !VariableAccessUtils.getVariableReferences(condition.getIterParam(), value).stream()
136           .map(ExpressionUtils::getCallForQualifier)
137           .allMatch(KEY_VALUE_GET_METHODS);
138       }
139
140       private void processMerge(MapCheckCondition condition,
141                                 PsiStatement existsBranch,
142                                 PsiStatement noneBranch) {
143         if(noneBranch instanceof PsiExpressionStatement && existsBranch instanceof PsiExpressionStatement) {
144           PsiExpression absentValue = extractPutValue(condition, noneBranch);
145           if (absentValue == null) return;
146           PsiExpression presentValue = extractPutValue(condition, existsBranch);
147           if (presentValue == null || !LambdaGenerationUtil.canBeUncheckedLambda(presentValue)) return;
148           // absentValue should not refer map
149           if (!PsiTreeUtil.processElements(absentValue, e -> !condition.isMap(e))) return;
150           boolean hasVariable = condition.hasVariable();
151           if (hasVariable && PsiTreeUtil.collectElements(presentValue, condition::isValueReference).length == 0) return;
152           PsiElement[] mapRefs = PsiTreeUtil.collectElements(presentValue, condition::isMap);
153           if(hasVariable ^ mapRefs.length == 0) return;
154           for(PsiElement mapRef : mapRefs) {
155             PsiElement parent = mapRef.getParent();
156             if (!(parent instanceof PsiReferenceExpression) || condition.extractGetCall(parent.getParent()) == null) return;
157           }
158           if (PsiTreeUtil.collectElements(presentValue, e -> PsiEquivalenceUtil.areElementsEquivalent(e, absentValue)).length == 0) {
159             return;
160           }
161           if (NullabilityUtil.getExpressionNullability(absentValue) == Nullability.NULLABLE ||
162               NullabilityUtil.getExpressionNullability(presentValue) == Nullability.NULLABLE) {
163             return;
164           }
165           boolean informationLevel =
166             !mySideEffects && SideEffectChecker.mayHaveSideEffects(presentValue, ex -> condition.extractGetCall(ex) != null);
167           register(condition, holder, informationLevel, new ReplaceWithSingleMapOperation("merge", PsiTreeUtil
168             .getParentOfType(absentValue, PsiMethodCallExpression.class), presentValue, noneBranch));
169         }
170       }
171
172       private void processGetPut(MapCheckCondition condition, PsiElement result, PsiElement exists, PsiElement none) {
173         PsiMethodCallExpression getCall = condition.extractGetCall(exists);
174         if(getCall == null) return;
175
176         if(!(none instanceof PsiExpression)) return;
177         PsiExpression noneExpression = (PsiExpression)none;
178         PsiMethodCallExpression putCall = extractMapMethodCall(noneExpression, "put");
179         if (mySuggestMapPutIfAbsent &&
180             putCall != null &&
181             condition.isGetNull() &&
182             condition.isMap(putCall.getMethodExpression().getQualifierExpression())) {
183           PsiExpression[] putArgs = putCall.getArgumentList().getExpressions();
184           if (putArgs.length != 2 || !condition.isKey(putArgs[0]) || !ExpressionUtils.isSafelyRecomputableExpression(putArgs[1])) return;
185           register(condition, holder, false, new ReplaceWithSingleMapOperation("putIfAbsent", getCall, putArgs[1], result));
186         }
187         if (mySuggestMapGetOrDefault && condition.isContainsKey() && ExpressionUtils.isSafelyRecomputableExpression(noneExpression) &&
188             condition.isMapValueType(noneExpression.getType())) {
189           register(condition, holder, false, new ReplaceWithSingleMapOperation("getOrDefault", getCall, noneExpression, result));
190         }
191       }
192
193       private void processSingleBranch(MapCheckCondition condition, PsiStatement noneBranch) {
194         PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(noneBranch);
195         if(assignment != null && mySuggestMapGetOrDefault && condition.isContainsKey()) {
196           /*
197             value = map.get(key);
198             if(value == null) {
199               value = ...
200             }
201            */
202           PsiExpression rValue = assignment.getRExpression();
203           if (ExpressionUtils.isSafelyRecomputableExpression(rValue) && condition.isValueReference(assignment.getLExpression()) &&
204               !condition.isValueReference(rValue) && condition.isMapValueType(rValue.getType())) {
205             register(condition, holder, false, ReplaceWithSingleMapOperation.fromIf("getOrDefault", condition, rValue));
206           }
207         } else if (condition.isGetNull()) {
208           /*
209             value = map.get(key);
210             if(value == null) {
211               value = ...
212               map.put(key, value);
213             }
214            */
215           PsiExpression lambdaCandidate = extractLambdaCandidate(condition, noneBranch);
216           if (lambdaCandidate != null && mySuggestMapComputeIfAbsent) {
217             if (NullabilityUtil.getExpressionNullability(lambdaCandidate) == Nullability.NULLABLE) return;
218             boolean informationLevel = !mySideEffects && SideEffectChecker.mayHaveSideEffects(lambdaCandidate);
219             register(condition, holder, informationLevel, ReplaceWithSingleMapOperation.fromIf("computeIfAbsent", condition, lambdaCandidate));
220           }
221           if (lambdaCandidate == null) {
222             PsiExpression expression = extractPutValue(condition, noneBranch);
223             if(expression != null) {
224               String replacement = null;
225               boolean informationLevel = false;
226               if (mySuggestMapPutIfAbsent && ExpressionUtils.isSafelyRecomputableExpression(expression) && !condition.isValueReference(expression)) {
227                 replacement = "putIfAbsent";
228               }
229               else if (mySuggestMapComputeIfAbsent && !condition.hasVariable()) {
230                 informationLevel = !mySideEffects && SideEffectChecker.mayHaveSideEffects(expression);
231                 replacement = "computeIfAbsent";
232               }
233               if(replacement != null) {
234                 if(condition.hasVariable()) {
235                   register(condition, holder, informationLevel, ReplaceWithSingleMapOperation.fromIf(replacement, condition, expression));
236                 } else {
237                   PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(expression, PsiMethodCallExpression.class);
238                   LOG.assertTrue(call != null);
239                   register(condition, holder, informationLevel, new ReplaceWithSingleMapOperation(replacement, call, expression, noneBranch));
240                 }
241               }
242             }
243           }
244         }
245       }
246     };
247   }
248
249
250   @NotNull
251   public static String getNameCandidate(String name) {
252     // Either last uppercase letter (if it's not the last letter) or the first letter, removing leading underscores
253     // token -> t
254     // myAccessToken -> t
255     // SQL -> s
256     // __name -> n
257     // __1 -> k
258     String nameCandidate;
259     name = name.replaceFirst("^[_\\d]+", "");
260     if (name.isEmpty()) return "k";
261     nameCandidate = name.substring(0, 1);
262     for (int pos = name.length() - 1; pos > 0; pos--) {
263       if (Character.isUpperCase(name.charAt(pos))) {
264         if (pos != name.length() - 1) {
265           nameCandidate = name.substring(pos, pos + 1);
266         }
267         break;
268       }
269     }
270     return StringUtil.toLowerCase(nameCandidate);
271   }
272
273   private static class ReplaceWithSingleMapOperation implements LocalQuickFix {
274     private final String myMethodName;
275     private final SmartPsiElementPointer<PsiMethodCallExpression> myCallPointer;
276     private final SmartPsiElementPointer<PsiExpression> myValuePointer;
277     private final SmartPsiElementPointer<PsiElement> myResultPointer;
278
279     ReplaceWithSingleMapOperation(String methodName, PsiMethodCallExpression call, PsiExpression value, PsiElement result) {
280       myMethodName = methodName;
281       SmartPointerManager manager = SmartPointerManager.getInstance(value.getProject());
282       myCallPointer = manager.createSmartPsiElementPointer(call);
283       myValuePointer = manager.createSmartPsiElementPointer(value);
284       myResultPointer = manager.createSmartPsiElementPointer(result);
285     }
286
287     @Override
288     public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
289       PsiElement outerElement = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiIfStatement.class,
290                                                             PsiConditionalExpression.class, PsiForeachStatement.class);
291       if (outerElement == null) return;
292       MapCondition condition = outerElement instanceof PsiForeachStatement ?
293                                MapLoopCondition.create((PsiForeachStatement)outerElement) :
294                                fromConditional(outerElement, true);
295       if(condition == null) return;
296       PsiMethodCallExpression call = myCallPointer.getElement();
297       if (call == null) return;
298       PsiExpressionList argsList = call.getArgumentList();
299       PsiExpression[] args = argsList.getExpressions();
300       if(args.length == 0) return;
301       if ((myMethodName.equals("merge") || myMethodName.equals("replaceAll")) && args.length != 2) return;
302       PsiExpression value = myValuePointer.getElement();
303       if (value == null) return;
304       PsiElement result = myResultPointer.getElement();
305       if(result == null) return;
306
307       PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
308       CommentTracker ct = new CommentTracker();
309       ExpressionUtils.bindCallTo(call, myMethodName);
310       PsiExpression replacement;
311       if(myMethodName.equals("computeIfAbsent")) {
312         PsiExpression key = args[0];
313         List<PsiReferenceExpression> refs = Collections.emptyList();
314         String nameCandidate = "k";
315         if(key instanceof PsiReferenceExpression && ((PsiReferenceExpression)key).getQualifier() == null) {
316           // try to use lambda parameter if key is simple reference and has the same type as map keys
317           PsiMethod method = call.resolveMethod();
318           if(method != null) {
319             PsiType argType = method.getParameterList().getParameters()[0].getType();
320             PsiType mapKeyType = call.resolveMethodGenerics().getSubstitutor().substitute(argType);
321             PsiType keyType = key.getType();
322
323             if(mapKeyType != null && keyType != null && keyType.isAssignableFrom(mapKeyType)) {
324               PsiElement target = ((PsiReferenceExpression)key).resolve();
325               refs = target == null ? Collections.emptyList() :
326                      StreamEx.of(PsiTreeUtil.collectElementsOfType(value, PsiReferenceExpression.class))
327                        .filter(ref -> ref.getQualifierExpression() == null && ref.isReferenceTo(target)).toList();
328               if (!refs.isEmpty()) {
329                 nameCandidate = getNameCandidate(((PsiReferenceExpression)key).getReferenceName());
330               }
331             }
332           }
333         }
334         String varName = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(nameCandidate, value, true);
335         for(PsiReferenceExpression ref : refs) {
336           ExpressionUtils.bindReferenceTo(ref, varName);
337         }
338         replacement = factory.createExpressionFromText(varName + " -> " + ct.text(value), value);
339       } else if (myMethodName.equals("merge")) {
340         MapCheckCondition checkCondition = ObjectUtils.tryCast(condition, MapCheckCondition.class);
341         if (checkCondition == null) return;
342         PsiExpression absentValue = args[1];
343         String aVar = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName("a", value, true);
344         String bVar = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName("b", value, true);
345         for(PsiElement e : PsiTreeUtil.collectElements(value, e -> PsiEquivalenceUtil.areElementsEquivalent(e, absentValue))) {
346           ct.replace(e, factory.createIdentifier(bVar));
347         }
348         for (PsiElement e : PsiTreeUtil
349           .collectElements(value, e -> checkCondition.extractGetCall(e) != null || checkCondition.isValueReference(e))) {
350           ct.replace(e, factory.createIdentifier(aVar));
351         }
352         replacement = factory.createExpressionFromText("("+aVar+","+bVar+") -> "+ct.text(value), value);
353       }
354       else if (myMethodName.equals("replaceAll")) {
355         MapLoopCondition loopCondition = ObjectUtils.tryCast(condition, MapLoopCondition.class);
356         if (loopCondition == null) return;
357         String kVar = suggestKeyName(loopCondition, value);
358         String vVar = new VariableNameGenerator(value, VariableKind.PARAMETER).byName("v", "value").generate(true);
359         replacement = createLambdaForLoopReplacement(factory, kVar, vVar, loopCondition, value, ct);
360         ct.delete(args);
361       }
362       else {
363         replacement = ct.markUnchanged(value);
364       }
365       PsiElement newArg;
366       if (args.length == 2 && !myMethodName.equals("merge") && !myMethodName.equals("replaceAll")) {
367         newArg = ct.replace(args[1], replacement);
368       } else {
369         newArg = argsList.add(replacement);
370       }
371       if(newArg instanceof PsiLambdaExpression) {
372         LambdaCanBeMethodReferenceInspection.replaceLambdaWithMethodReference((PsiLambdaExpression)newArg);
373       }
374       if (PsiTreeUtil.isAncestor(outerElement, result, true)) {
375         result = ct.replaceAndRestoreComments(outerElement, result);
376       } else {
377         ct.deleteAndRestoreComments(outerElement);
378       }
379       PsiVariable variable = condition instanceof MapCheckCondition ? ((MapCheckCondition)condition).extractDeclaration() : null;
380       if (variable != null && !PsiTreeUtil.isAncestor(result, variable, true) && ReferencesSearch.search(variable).findFirst() == null) {
381         new CommentTracker().deleteAndRestoreComments(variable);
382       }
383       CodeStyleManager.getInstance(project).reformat(result);
384     }
385
386     @NotNull
387     private static String suggestKeyName(@NotNull MapLoopCondition loopCondition, @NotNull PsiElement value) {
388       VariableNameGenerator generator = new VariableNameGenerator(value, VariableKind.PARAMETER);
389       if (!loopCondition.isEntrySet()) {
390         String origName = loopCondition.getIterParam().getName();
391         if (origName != null) {
392           String nameCandidate = getNameCandidate(origName);
393           if (origName.equals(nameCandidate)) return nameCandidate;
394           generator.byName(nameCandidate);
395         }
396       }
397       return generator.byName("k", "key").generate(true);
398     }
399
400     @NotNull
401     private static PsiExpression createLambdaForLoopReplacement(@NotNull PsiElementFactory factory,
402                                                                 @NotNull String kVar,
403                                                                 @NotNull String vVar,
404                                                                 @NotNull MapLoopCondition loopCondition,
405                                                                 @NotNull PsiExpression value,
406                                                                 @NotNull CommentTracker tracker) {
407       if (value instanceof PsiMethodCallExpression) {
408         if (loopCondition.isKeyAccess(value)) return factory.createExpressionFromText("(" + kVar + "," + vVar + ") ->" + kVar, value);
409         if (loopCondition.isValueAccess(value)) return factory.createExpressionFromText("(" + kVar + "," + vVar + ") ->" + vVar, value);
410       }
411       if (!loopCondition.isEntrySet()) {
412         PsiParameter param = loopCondition.getIterParam();
413         VariableAccessUtils.getVariableReferences(param, value).forEach(ref -> ExpressionUtils.bindReferenceTo(ref, kVar));
414       }
415       Collection<PsiMethodCallExpression> calls = PsiTreeUtil.collectElementsOfType(value, PsiMethodCallExpression.class);
416       for (PsiMethodCallExpression call : calls) {
417         if (loopCondition.isKeyAccess(call)) {
418           tracker.replace(call, kVar);
419         }
420         else if (loopCondition.isValueAccess(call)) {
421           tracker.replace(call, vVar);
422         }
423       }
424       return factory.createExpressionFromText("(" + kVar + "," + vVar + ") ->" + tracker.text(value), value);
425     }
426
427     @Nls
428     @NotNull
429     @Override
430     public String getName() {
431       return QuickFixBundle.message("java.8.map.api.inspection.fix.text", myMethodName);
432     }
433
434     @Nls
435     @NotNull
436     @Override
437     public String getFamilyName() {
438       return QuickFixBundle.message("java.8.map.api.inspection.fix.family.name");
439     }
440
441     @NotNull
442     static ReplaceWithSingleMapOperation fromIf(String methodName, MapCheckCondition condition, PsiExpression value) {
443       PsiMethodCallExpression call = condition.getCheckCall();
444       return create(methodName, call, value);
445     }
446
447     @NotNull
448     static ReplaceWithSingleMapOperation create(String methodName, PsiMethodCallExpression call, PsiExpression value) {
449       PsiStatement result = PsiTreeUtil.getParentOfType(call, PsiStatement.class);
450       LOG.assertTrue(result != null);
451       return new ReplaceWithSingleMapOperation(methodName, call, value, result);
452     }
453   }
454
455   private static void register(MapCheckCondition condition, ProblemsHolder holder, boolean informationLevel, ReplaceWithSingleMapOperation fix) {
456     if (informationLevel && !holder.isOnTheFly()) return;
457     holder.registerProblem(condition.getFullCondition(), QuickFixBundle.message("java.8.map.api.inspection.description", fix.myMethodName),
458                            informationLevel ? ProblemHighlightType.INFORMATION : ProblemHighlightType.GENERIC_ERROR_OR_WARNING, fix);
459   }
460
461 }