From cb0c00dc0a8277cfae02b780a6dc85a19683c753 Mon Sep 17 00:00:00 2001 From: "Rustam.Vishnyakov" Date: Fri, 24 Dec 2010 17:03:44 +0300 Subject: [PATCH 1/4] IDEA-63133 Map help buttons of the Libraries and Usage Scope pages of the settings dialog (under JavaScript) --- .../scriptingContext/LangScriptingContextConfigurable.java | 6 +++++- .../scriptingContext/ui/ScriptingContextsConfigurable.java | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java index 062a0b92ea29..10e0aca11405 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java @@ -24,6 +24,7 @@ import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.libraries.scripting.ScriptingLibraryManager; import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -38,7 +39,7 @@ public abstract class LangScriptingContextConfigurable implements Configurable, public LangScriptingContextConfigurable(Project project, LangScriptingContextProvider provider) { myLibManager = new ScriptingLibraryManager(project, provider.getLibraryType()); myPanel = useDedicatedLibraryUI(project) ? new ScriptingLibrariesPanel(provider, project, myLibManager) : new ScriptingLibrariesPanelStub(); - myContextsConfigurable = new ScriptingContextsConfigurable(project, provider.getLibraryMappings(project)); + myContextsConfigurable = new ScriptingContextsConfigurable(this, project, provider.getLibraryMappings(project)); } private static boolean useDedicatedLibraryUI(Project project) { @@ -88,4 +89,7 @@ public abstract class LangScriptingContextConfigurable implements Configurable, public Configurable[] getConfigurables() { return new Configurable[] {myContextsConfigurable}; } + + @Nullable + public abstract String getUsageScopeHelpTopic(); } diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingContextsConfigurable.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingContextsConfigurable.java index 1568aad46ea5..a7585f610658 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingContextsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingContextsConfigurable.java @@ -16,6 +16,7 @@ package com.intellij.ide.scriptingContext.ui; import com.intellij.ide.IdeBundle; +import com.intellij.ide.scriptingContext.LangScriptingContextConfigurable; import com.intellij.ide.scriptingContext.ScriptingLibraryMappings; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.libraries.scripting.ScriptingLibraryTable; @@ -32,14 +33,16 @@ import javax.swing.*; public class ScriptingContextsConfigurable extends LanguagePerFileConfigurable { private final ScriptingLibraryMappings myScriptingLibraryMappings; + private final LangScriptingContextConfigurable myParent; - public ScriptingContextsConfigurable(final Project project, final ScriptingLibraryMappings mappings) { + public ScriptingContextsConfigurable(final LangScriptingContextConfigurable parent, final Project project, final ScriptingLibraryMappings mappings) { super(project, ScriptingLibraryTable.LibraryModel.class, mappings, IdeBundle.message("scripting.lib.usageScope.caption"), IdeBundle.message("scripting.lib.usageScope.tableTitle"), IdeBundle.message("scripting.lib.usageScope.override.question"), IdeBundle.message("scripting.lib.usageScope.override.title")); myScriptingLibraryMappings = mappings; + myParent = parent; } public void resetMappings() { @@ -81,6 +84,6 @@ public class ScriptingContextsConfigurable extends LanguagePerFileConfigurable Date: Fri, 24 Dec 2010 16:16:54 +0100 Subject: [PATCH 2/4] cleanup --- .../StringConcatenationInFormatCall.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/StringConcatenationInFormatCall.html b/plugins/InspectionGadgets/src/inspectionDescriptions/StringConcatenationInFormatCall.html index e6ccbd2fd643..05ec38304f5e 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/StringConcatenationInFormatCall.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/StringConcatenationInFormatCall.html @@ -1,7 +1,7 @@ -
+ -This inspection reports non-constant string concatenations used as a Format string argument. +This inspection reports non-constant string concatenations used as a format string argument. Often this is the result of mistakenly concatenating a string format argument by typing a '+' when a ',' was meant. This inspection checks calls to appropriate methods on @@ -9,5 +9,7 @@ This inspection checks calls to appropriate methods on java.lang.String, java.io.PrintWriter, or java.io.PrintStream. -
New in 10.1, Powered by InspectionGadgets
+ +

New in 10.1, Powered by InspectionGadgets + \ No newline at end of file From a07311a33518220745123fdba6ac4c7b646c3d8a Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 24 Dec 2010 16:17:30 +0100 Subject: [PATCH 3/4] tweak quickfix --- .../ig/bugs/StringConcatenationInFormatCallInspection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspection.java index 4d0caaf16b46..d8aaafed24f0 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/StringConcatenationInFormatCallInspection.java @@ -86,7 +86,7 @@ public class StringConcatenationInFormatCallInspection extends BaseInspection { if (rhs == null) { return; } - parent.addAfter(rhs, binaryExpression); + parent.add(rhs); parent.addAfter(lhs, binaryExpression); binaryExpression.delete(); } From 3d8c4c9ccebede9e9eeb28a2396ad1e6829d8856 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Fri, 24 Dec 2010 16:17:43 +0100 Subject: [PATCH 4/4] IDEA-26396 (IDEA should highlight dangerous usages of MessageFormat.format method) --- .../siyeh/InspectionGadgetsBundle.properties | 4 +- .../com/siyeh/ig/InspectionGadgetsPlugin.java | 1 + ...tenationInMessageFormatCallInspection.java | 230 ++++++++++++++++++ ...tringConcatenationInMessageFormatCall.html | 10 + 4 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/bugs/StringConcatenationInMessageFormatCallInspection.java create mode 100644 plugins/InspectionGadgets/src/inspectionDescriptions/StringConcatenationInMessageFormatCall.html diff --git a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties index cbf3b979dbb7..422d4dfa529f 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1821,5 +1821,7 @@ simplifiable.annotation.problem.descriptor=Annotation #ref may be r simplifiable.annotation.quickfix=Simplify annotation overloaded.methods.with.same.number.parameters.option=Ignore overrides of library methods string.concatenation.in.format.call.display.name=String concatenation argument in call to 'format()' -string.concatenation.in.format.call.problem.descriptor=Non-constant string concatenation argument in call to 'format()' #loc +string.concatenation.in.format.call.problem.descriptor=String concatenation argument in call to 'format()' #loc string.concatenation.in.format.call.quickfix=Pass ''{0}'' as argument to ''format()'' +string.concatenation.in.message.format.call.display.name=String concatenation argument in call to 'MessageFormat.format()' +string.concatenation.in.message.format.call.problem.descriptor=String concatenation argument in call to 'MessageFormat.format()' \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java b/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java index 0407dea5dea6..67bfa14a3c64 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java @@ -564,6 +564,7 @@ public class InspectionGadgetsPlugin implements ApplicationComponent, m_inspectionClasses.add(ReturnNullInspection.class); m_inspectionClasses.add(StaticCallOnSubclassInspection.class); m_inspectionClasses.add(StringConcatenationInFormatCallInspection.class); + m_inspectionClasses.add(StringConcatenationInMessageFormatCallInspection.class); m_inspectionClasses.add(StaticFieldReferenceOnSubclassInspection.class); m_inspectionClasses.add(StringEqualityInspection.class); m_inspectionClasses.add(SubtractionInCompareToInspection.class); diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/StringConcatenationInMessageFormatCallInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/StringConcatenationInMessageFormatCallInspection.java new file mode 100644 index 000000000000..eb26246ba9d6 --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/StringConcatenationInMessageFormatCallInspection.java @@ -0,0 +1,230 @@ +/* + * Copyright 2010 Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.bugs; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ClassUtils; +import com.siyeh.ig.psiutils.ExpressionUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class StringConcatenationInMessageFormatCallInspection + extends BaseInspection { + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "string.concatenation.in.message.format.call.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "string.concatenation.in.message.format.call.problem.descriptor"); + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression) infos[0]; + final String referenceName = referenceExpression.getReferenceName(); + return new StringConcatenationInFormatCallFix(referenceName); + } + + private static class StringConcatenationInFormatCallFix + extends InspectionGadgetsFix { + + private final String variableName; + + public StringConcatenationInFormatCallFix(String variableName) { + this.variableName = variableName; + } + + @NotNull + public String getName() { + return InspectionGadgetsBundle.message( + "string.concatenation.in.format.call.quickfix", variableName); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) + throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + if (!(element instanceof PsiBinaryExpression)) { + return; + } + final PsiBinaryExpression binaryExpression = + (PsiBinaryExpression) element; + final PsiElement parent = binaryExpression.getParent(); + if (!(parent instanceof PsiExpressionList)) { + return; + } + final PsiExpressionList expressionList = (PsiExpressionList) parent; + final PsiExpression lhs = binaryExpression.getLOperand(); + final PsiExpression rhs = binaryExpression.getROperand(); + if (rhs == null) { + return; + } + final PsiExpression[] expressions = expressionList.getExpressions(); + final int parameter = expressions.length - 1; + expressionList.add(rhs); + final Object constant = + ExpressionUtils.computeConstantExpression(lhs); + if (constant instanceof String) { + final PsiExpression newExpression = + addParameter(lhs, parameter); + if (newExpression == null) { + expressionList.addAfter(lhs, binaryExpression); + } else { + expressionList.addAfter(newExpression, binaryExpression); + } + } else { + expressionList.addAfter(lhs, binaryExpression); + } + binaryExpression.delete(); + } + + private static PsiExpression addParameter(PsiExpression expression, + int parameterNumber) { + if (expression instanceof PsiBinaryExpression) { + final PsiBinaryExpression binaryExpression = + (PsiBinaryExpression) expression; + final PsiExpression rhs = binaryExpression.getROperand(); + if (rhs == null) { + return null; + } + final PsiExpression newExpression = + addParameter(rhs, parameterNumber); + if (newExpression == null) { + return null; + } + rhs.replace(newExpression); + return expression; + } else if (expression instanceof PsiLiteralExpression) { + final PsiLiteralExpression literalExpression = + (PsiLiteralExpression) expression; + final Object value = literalExpression.getValue(); + if (!(value instanceof String)) { + return null; + } + final Project project = expression.getProject(); + final PsiElementFactory factory = + JavaPsiFacade.getElementFactory(project); + return factory.createExpressionFromText("\"" + value + '{' + + parameterNumber + "}\"", null); + } else { + return null; + } + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new StringConcatenationInFormatCallVisitor(); + } + + private static class StringConcatenationInFormatCallVisitor + extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression( + PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = + expression.getMethodExpression(); + if (!isMessageFormatCall(methodExpression)) { + return; + } + + final PsiExpressionList argumentList = expression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if(arguments.length == 0){ + return; + } + final PsiExpression firstArgument = arguments[0]; + final PsiType type = firstArgument.getType(); + if(type == null){ + return; + } + final int formatArgumentIndex; + if("java.util.Locale".equals(type.getCanonicalText()) + && arguments.length > 1){ + formatArgumentIndex = 1; + } else{ + formatArgumentIndex = 0; + } + final PsiExpression formatArgument = arguments[formatArgumentIndex]; + final PsiType formatArgumentType = formatArgument.getType(); + if (formatArgumentType == null || + !formatArgumentType.equalsToText("java.lang.String")) { + return; + } + if (!(formatArgument instanceof PsiBinaryExpression)) { + return; + } + if (PsiUtil.isConstantExpression(formatArgument)) { + return; + } + + final PsiBinaryExpression binaryExpression = + (PsiBinaryExpression) formatArgument; + final PsiExpression lhs = binaryExpression.getLOperand(); + final PsiType lhsType = lhs.getType(); + if (lhsType == null || !lhsType.equalsToText("java.lang.String")) { + return; + } + final PsiExpression rhs = binaryExpression.getROperand(); + if (!(rhs instanceof PsiReferenceExpression)) { + return; + } + registerError(formatArgument, rhs); + } + + private static boolean isMessageFormatCall( + PsiReferenceExpression methodExpression) { + @NonNls final String referenceName = + methodExpression.getReferenceName(); + if (!"format".equals(referenceName)) { + return false; + } + final PsiExpression qualifierExpression = + methodExpression.getQualifierExpression(); + if (!(qualifierExpression instanceof PsiReferenceExpression)) { + return false; + } + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression) qualifierExpression; + final PsiElement target = referenceExpression.resolve(); + if (!(target instanceof PsiClass)) { + return false; + } + final PsiClass aClass = (PsiClass) target; + return ClassUtils.isSubclass(aClass, "java.text.MessageFormat"); + } + } +} diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/StringConcatenationInMessageFormatCall.html b/plugins/InspectionGadgets/src/inspectionDescriptions/StringConcatenationInMessageFormatCall.html new file mode 100644 index 000000000000..da1c830b3dce --- /dev/null +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/StringConcatenationInMessageFormatCall.html @@ -0,0 +1,10 @@ + + + +This inspection reports non-constant string concatenations used as an argument to a call to +MessageFormat.format(). +Often this is the result of mistakenly concatenating a string format argument by typing +a '+' when a ',' was meant. +

+New in 10.1, Powered by InspectionGadgets + \ No newline at end of file