From 5bc252d813a4582332ddddf20b2ce213588c3ba4 Mon Sep 17 00:00:00 2001 From: AnonymousAccount4SE <33093740+Michaelll123@users.noreply.github.com> Date: Sun, 7 Apr 2024 15:21:16 +0800 Subject: [PATCH] Improving the Names Recommended by Extract Local Variable Refactoring. Addressing Issue 745. --- .../ExtractLocalVariableRefactoringTest.java | 35 ++- .../AbortSearchException.java | 18 ++ .../ConvertLoopOperation.java | 118 ++++++++ .../ExtractLocalVariableRefactoring.java | 284 +++++++++++++++++- 4 files changed, 450 insertions(+), 5 deletions(-) create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/AbortSearchException.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ConvertLoopOperation.java diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringTest.java index 7aebbb10c79..ef70433b82c 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractlocalvariable/ExtractLocalVariableRefactoringTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2016 Institute for Software, HSR Hochschule fuer Technik + * Copyright (c) 2008, 2024 Institute for Software, HSR Hochschule fuer Technik * Rapperswil, University of applied sciences and others * * This program and the accompanying materials @@ -14,6 +14,7 @@ * Sergey Prigogin (Google) * Marc-Andre Laperle (Ericsson) * Thomas Corbat (IFS) + * Taiming Wang - Name recommendation improvement *******************************************************************************/ package org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable; @@ -593,4 +594,36 @@ public void testSuggestedNameCFile_Bug412032_2() throws Exception { public void testTemplateWithFunctionArgument_487186() throws Exception { assertRefactoringSuccess(); } + + //A.cpp + //using namespace std; + //String getProgramResolveLink(String program){ + //return program; + //} + //void useProgram(String program) + //{ + //cout << getProgramResolveLink(program); + //} + //==================== + //using namespace std; + //String getProgramResolveLink(String program){ + //return program; + //} + //void useProgram(String program) + //{ + // void programResolveLink = getProgramResolveLink(program); + // cout << programResolveLink; + //} + + //refScript.xml + // + // + // + // + public void testIfRecommend() throws Exception { + assertRefactoringSuccess(); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/AbortSearchException.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/AbortSearchException.java new file mode 100644 index 00000000000..2f56a0c7703 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/AbortSearchException.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2024 + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Taiming Wang - Initial implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable; + +public class AbortSearchException extends RuntimeException { + private static final long serialVersionUID = 8809979732051907351L; +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ConvertLoopOperation.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ConvertLoopOperation.java new file mode 100644 index 00000000000..748fa7711d6 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ConvertLoopOperation.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2024 + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Taiming Wang - Initial implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable; + +import java.util.AbstractMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ConvertLoopOperation { + protected static final String FOR_LOOP_ELEMENT_IDENTIFIER = "element"; //$NON-NLS-1$ + + private static final Map IRREG_NOUNS = Stream + .of(new AbstractMap.SimpleImmutableEntry<>("Children", "Child"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Entries", "Entry"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Proxies", "Proxy"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Indices", "Index"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("People", "Person"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Properties", "Property"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Factories", "Factory"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Archives", "archive"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Aliases", "Alias"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Alternatives", "Alternative"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Capabilities", "Capability"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Hashes", "Hash"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Directories", "Directory"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Statuses", "Status"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Instances", "Instance"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Classes", "Class"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Deliveries", "Delivery"), //$NON-NLS-1$ //$NON-NLS-2$ + new AbstractMap.SimpleImmutableEntry<>("Vertices", "Vertex")) //$NON-NLS-1$ //$NON-NLS-2$ + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + private static final Set NO_BASE_TYPES = Stream.of("integers", //$NON-NLS-1$ + "floats", //$NON-NLS-1$ + "doubles", //$NON-NLS-1$ + "booleans", //$NON-NLS-1$ + "bytes", //$NON-NLS-1$ + "chars", //$NON-NLS-1$ + "shorts", //$NON-NLS-1$ + "longs") //$NON-NLS-1$ + .collect(Collectors.toSet()); + + private static final Set CUT_PREFIX = Stream.of("all") //$NON-NLS-1$ + .collect(Collectors.toSet()); + + private static final Set IRREG_ENDINGS = Stream.of("xes", //$NON-NLS-1$ + "ies", //$NON-NLS-1$ + "oes", //$NON-NLS-1$ + "ses", //$NON-NLS-1$ + "hes", //$NON-NLS-1$ + "zes", //$NON-NLS-1$ + "ves", //$NON-NLS-1$ + "ces", //$NON-NLS-1$ + "ss", //$NON-NLS-1$ + "is", //$NON-NLS-1$ + "us", //$NON-NLS-1$ + "os", //$NON-NLS-1$ + "as") //$NON-NLS-1$ + .collect(Collectors.toSet()); + + public static String modifyBaseName(String suggestedName) { + String name = suggestedName; + for (String prefix : CUT_PREFIX) { + if (prefix.length() >= suggestedName.length()) { + continue; + } + char afterPrefix = suggestedName.charAt(prefix.length()); + if (Character.isUpperCase(afterPrefix) || afterPrefix == '_') { + if (suggestedName.toLowerCase().startsWith(prefix)) { + String nameWithoutPrefix = suggestedName.substring(prefix.length()); + if (nameWithoutPrefix.startsWith("_") && nameWithoutPrefix.length() > 1) { //$NON-NLS-1$ + name = nameWithoutPrefix.substring(1); + } else { + name = nameWithoutPrefix; + } + if (name.length() == 1) { + return name; + } + break; + } + } + } + for (Map.Entry entry : IRREG_NOUNS.entrySet()) { + String suffix = entry.getKey(); + if (name.toLowerCase().endsWith(suffix.toLowerCase())) { + String firstPart = name.substring(0, name.length() - suffix.length()); + return firstPart + entry.getValue(); + } + } + for (String varname : NO_BASE_TYPES) { + if (name.equalsIgnoreCase(varname)) { + return FOR_LOOP_ELEMENT_IDENTIFIER; + } + } + for (String suffix : IRREG_ENDINGS) { + if (name.toLowerCase().endsWith(suffix)) { + return FOR_LOOP_ELEMENT_IDENTIFIER; + } + } + if (name.length() > 2 && name.endsWith("s")) { //$NON-NLS-1$ + return name.substring(0, name.length() - 1); + } + return FOR_LOOP_ELEMENT_IDENTIFIER; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoring.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoring.java index 8d153c408ab..df83f2fd3a3 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoring.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractlocalvariable/ExtractLocalVariableRefactoring.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2016 Google and others. + * Copyright (c) 2008, 2024 Google and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -12,6 +12,7 @@ * Tom Ball (Google) - Initial API and implementation * Sergey Prigogin (Google) * Marc-Andre Laperle (Ericsson) + * Taiming Wang - Name recommendation improvement *******************************************************************************/ package org.eclipse.cdt.internal.ui.refactoring.extractlocalvariable; @@ -21,6 +22,7 @@ import java.util.Map; import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression; import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement; import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; @@ -28,11 +30,14 @@ import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer; import org.eclipse.cdt.core.dom.ast.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTFieldReference; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTForStatement; import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTIdExpression; +import org.eclipse.cdt.core.dom.ast.IASTInitializer; +import org.eclipse.cdt.core.dom.ast.IASTInitializerClause; import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; @@ -55,6 +60,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTIdExpression; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPFunction; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPScope.CPPScopeProblem; import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; import org.eclipse.cdt.internal.ui.refactoring.CRefactoringDescriptor; import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector; @@ -91,6 +97,70 @@ public class ExtractLocalVariableRefactoring extends CRefactoring { private final VariableNameInformation info; private NodeContainer container; + /* + * Retrieve variable names that are initialized by the to-be-extracted expression for reuse. + */ + private static final class LocalVariableWithIdenticalExpressionFinder extends ASTVisitor { + private ArrayList usedNames; + + private IASTNode expression; + + private Boolean ifStopOnFirstMatch; + + public LocalVariableWithIdenticalExpressionFinder(IASTNode expression, Boolean ifStopOnFirstMatch) { + usedNames = new ArrayList<>(); + this.expression = expression; + this.ifStopOnFirstMatch = ifStopOnFirstMatch; + super.shouldVisitDeclarators = true; + } + + public String getUsedName() { + return this.usedNames.get(0); + } + + public void setUsedName(String usedName) { + this.usedNames.add(usedName); + } + + @Override + public int visit(IASTDeclarator node) { + IASTInitializer initializer = node.getInitializer(); + if (initializer != null) { + if (initializer.getRawSignature().equals("= " + this.expression.getRawSignature())) { //$NON-NLS-1$ + setUsedName(node.getName().getRawSignature()); + if (this.ifStopOnFirstMatch.booleanValue()) + throw new AbortSearchException(); + } + } + return super.visit(node); + } + } + + /** + * Retrieves a used name in the whole compilation unit that is assigned with the same + * expression. + * + * @param selected the selected node + * + * @return a used variable name for recommending new names. + */ + private String getUsedNameForIdenticalExpressionInCu(IASTNode selected) { + IASTNode surroundingBlock = selected; + surroundingBlock = ASTQueries.findAncestorWithType(surroundingBlock, IASTTranslationUnit.class); + if (surroundingBlock == null) { + return null; + } + LocalVariableWithIdenticalExpressionFinder localVariableWithIdenticalExpressionFinder = new LocalVariableWithIdenticalExpressionFinder( + selected, Boolean.TRUE); + String usedNameForIdenticalExpressionInCu = null; + try { + surroundingBlock.accept(localVariableWithIdenticalExpressionFinder); + } catch (AbortSearchException e) { + usedNameForIdenticalExpressionInCu = localVariableWithIdenticalExpressionFinder.getUsedName(); + } + return usedNameForIdenticalExpressionInCu; + } + public ExtractLocalVariableRefactoring(ICElement element, ISelection selection, ICProject project) { super(element, selection, project); info = new VariableNameInformation(); @@ -134,7 +204,7 @@ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) NodeHelper.findMethodContext(container.getNodesToWrite().get(0), refactoringContext, sm); sm.worked(1); - info.setName(guessTempName()); + info.setName(guessTempNameWithContext()); sm.done(); return initStatus; @@ -238,6 +308,14 @@ public int visit(IASTExpression expression) { return container; } + private static String getBaseNameFromReceiver(IASTName receiver) { + if (receiver != null) { + String receiverStr = receiver.toString(); + return ConvertLoopOperation.modifyBaseName(receiverStr); + } + return "element"; //$NON-NLS-1$ + } + private boolean isNodeInsideSelection(IASTNode node) { return node.isPartOfTranslationUnitFile() && SelectionHelper.isNodeInsideRegion(node, selectedRegion); } @@ -315,12 +393,200 @@ public String guessTempName() { } } + public String guessTempNameWithContext() { + String[] proposals = guessTempNamesWithContext(); + if (proposals.length == 0) { + return info.getName(); + } else { + String name = proposals[0]; + return name; + } + } + private String[] getPrefixes() { // In Future we could use user preferences to define the prefixes - String[] prefixes = { "get", "is" }; //$NON-NLS-1$//$NON-NLS-2$ + String[] prefixes = { "get", "is", "to", "create", "load", "find", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$//$NON-NLS-5$//$NON-NLS-6$ + "build", "generate", "prepare", "parse", "current", "resolve", "retrieve", "make", "add", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$//$NON-NLS-5$//$NON-NLS-6$//$NON-NLS-7$//$NON-NLS-8$//$NON-NLS-9$ + "extract", "select", "allocate", "write" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + return prefixes; } + /** + * @return proposed variable names based on context (may be empty, but not null). The first proposal should be + * used as "best guess" (if it exists). + */ + public String[] guessTempNamesWithContext() { + + final List guessedTempNames = new ArrayList<>(); + final List usedNames = new ArrayList<>(); + IASTFunctionDefinition funcDef = ASTQueries.findAncestorWithType(target, IASTFunctionDefinition.class); + final IScope scope; + if (funcDef != null && funcDef.getBody() instanceof IASTCompoundStatement) { + IASTCompoundStatement body = (IASTCompoundStatement) funcDef.getBody(); + scope = body.getScope(); + } else { + scope = null; + } + + if (target != null) { + + target.accept(new ASTVisitor() { + { + shouldVisitNames = true; + shouldVisitExpressions = true; + } + + @Override + public int visit(IASTName name) { + addTempName(name.getLastName().toString()); + return super.visit(name); + } + + @Override + public int visit(IASTExpression expression) { + // If the expression starts with a function call with a name, we should only + // need to guess this name + if (expression == target && expression instanceof IASTFunctionCallExpression) { + String usedNameForIdenticalExpressionInCu = getUsedNameForIdenticalExpressionInCu(expression); + IASTFunctionCallExpression functionCallExpression = (IASTFunctionCallExpression) expression; + IASTExpression functionNameExpression = functionCallExpression.getFunctionNameExpression(); + if (functionNameExpression instanceof IASTFieldReference) { + IASTFieldReference fieldReference = (IASTFieldReference) functionNameExpression; + IASTExpression fieldOwner = fieldReference.getFieldOwner(); + IASTName field = fieldReference.getFieldName(); + + IASTInitializerClause[] arguments = functionCallExpression.getArguments(); + Boolean hasFunctionCalls = false; + for (IASTInitializerClause argument : arguments) { + if (argument instanceof IASTFunctionCallExpression) { + hasFunctionCalls = true; + } + } + if ((arguments.length > 1 || hasFunctionCalls || target.getRawSignature().length() >= 30) + && usedNameForIdenticalExpressionInCu != null) { + addTempName(usedNameForIdenticalExpressionInCu); + } else if (fieldOwner instanceof IASTIdExpression) { + String name = field.getLastName().toString(); + if (name.equals("begin") || name.equals("end") || name.equals("get")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + IASTIdExpression idExpression = (IASTIdExpression) fieldOwner; + String modifiedName = getBaseNameFromReceiver(idExpression.getName().getLastName()); + if (!modifiedName.equals("element")) //$NON-NLS-1$ + addTempName(modifiedName); + } + } else { + addTempName(field.getLastName().toString()); + } + + } + + if (functionNameExpression instanceof IASTIdExpression) { + IASTIdExpression idExpression = (IASTIdExpression) functionNameExpression; + + if (idExpression.getName() != null) { + IASTInitializerClause[] arguments = functionCallExpression.getArguments(); + Boolean hasFunctionCalls = false; + for (IASTInitializerClause argument : arguments) { + if (argument instanceof IASTFunctionCallExpression) { + hasFunctionCalls = true; + } + } + if ((arguments.length > 1 || hasFunctionCalls + || target.getRawSignature().length() >= 30) + && usedNameForIdenticalExpressionInCu != null) { + addTempName(usedNameForIdenticalExpressionInCu); + } else { + addTempName(idExpression.getName().getLastName().toString()); + } + if (guessedTempNames.size() > 0) { + return PROCESS_ABORT; + } + } + } + } + + else if (expression instanceof IASTLiteralExpression) { + IASTLiteralExpression literal = (IASTLiteralExpression) expression; + String name = null; + switch (literal.getKind()) { + case IASTLiteralExpression.lk_char_constant: + name = "c"; //$NON-NLS-1$ + break; + case IASTLiteralExpression.lk_float_constant: + name = "f"; //$NON-NLS-1$ + break; + case IASTLiteralExpression.lk_integer_constant: + name = "i"; //$NON-NLS-1$ + break; + case IASTLiteralExpression.lk_string_literal: + name = literal.toString(); + break; + case IASTLiteralExpression.lk_false: + case IASTLiteralExpression.lk_true: + name = "b"; //$NON-NLS-1$ + break; + } + if (name != null) { + addTempName(name); + } + } else if (expression instanceof IASTFieldReference) { + String usedNameForIdenticalExpressionInCu = getUsedNameForIdenticalExpressionInCu(expression); + if (target.getRawSignature().length() >= 10 && usedNameForIdenticalExpressionInCu != null) { + addTempName(usedNameForIdenticalExpressionInCu); + } else { + addTempName(((IASTFieldReference) expression).getFieldName().getLastName().toString()); + } + + } else if (expression instanceof IASTArraySubscriptExpression) { + IASTArraySubscriptExpression astSubscriptExpression = (IASTArraySubscriptExpression) expression; + IASTExpression arrayExpression = astSubscriptExpression.getArrayExpression(); + if (arrayExpression instanceof IASTIdExpression) { + IASTIdExpression idExpression = (IASTIdExpression) arrayExpression; + String modifiedName = getBaseNameFromReceiver(idExpression.getName().getLastName()); + if (!modifiedName.equals("element")) //$NON-NLS-1$ + addTempName(modifiedName); + } + + } + return super.visit(expression); + } + + private void addTempName(String name) { + name = trimPrefixes(name); + + IPreferencesService preferences = Platform.getPreferencesService(); + int capitalization = preferences.getInt(CUIPlugin.PLUGIN_ID, + PreferenceConstants.NAME_STYLE_VARIABLE_CAPITALIZATION, + PreferenceConstants.NAME_STYLE_CAPITALIZATION_LOWER_CAMEL_CASE, null); + String wordDelimiter = preferences.getString(CUIPlugin.PLUGIN_ID, + PreferenceConstants.NAME_STYLE_VARIABLE_WORD_DELIMITER, "", null); //$NON-NLS-1$ + String prefix = preferences.getString(CUIPlugin.PLUGIN_ID, + PreferenceConstants.NAME_STYLE_VARIABLE_PREFIX, "", null); //$NON-NLS-1$ + String suffix = preferences.getString(CUIPlugin.PLUGIN_ID, + PreferenceConstants.NAME_STYLE_VARIABLE_SUFFIX, "", null); //$NON-NLS-1$ + NameComposer composer = new NameComposer(capitalization, wordDelimiter, prefix, suffix); + name = composer.compose(name); + + if (name.length() > 0) { + if (nameAvailable(name, guessedTempNames, scope)) { + guessedTempNames.add(name); + } else { + usedNames.add(name); + } + } + } + }); + + } + if (guessedTempNames.isEmpty()) { + String name = makeTempName(usedNames, scope); + if (name != null) { + guessedTempNames.add(name); + } + } + return guessedTempNames.toArray(new String[guessedTempNames.size()]); + } + /** * @return proposed variable names (may be empty, but not null). The first * proposal should be used as "best guess" (if it exists). @@ -469,7 +735,17 @@ private boolean nameAvailable(String name, List guessedNames, IScope sco } if (scope != null) { IBinding[] bindings = scope.find(name); - return bindings == null || bindings.length == 0; + if (bindings == null || bindings.length == 0) { + return true; + } else { + if (bindings.length == 1) { + IBinding binding = bindings[0]; + if (binding instanceof CPPScopeProblem) { + return true; + } + } + return false; + } } return true; // No name references found }