mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-29 19:45:01 +02:00
Merge branch 'master' into sd90
This commit is contained in:
commit
5e5f1a61f7
6 changed files with 145 additions and 200 deletions
|
@ -8,13 +8,12 @@
|
|||
* Contributors:
|
||||
* Alena Laskavaia - initial API and implementation
|
||||
* Patrick Hofer [bug 315528]
|
||||
* Sergey Prigogin (Google)
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.codan.internal.checkers;
|
||||
|
||||
import org.eclipse.cdt.codan.checkers.CodanCheckersActivator;
|
||||
import org.eclipse.cdt.codan.core.cxx.model.AbstractIndexAstChecker;
|
||||
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
|
||||
import org.eclipse.cdt.core.dom.ast.DOMException;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTName;
|
||||
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
||||
|
@ -33,183 +32,85 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
|
|||
* @author Alena Laskavaia
|
||||
*/
|
||||
public class NonVirtualDestructor extends AbstractIndexAstChecker {
|
||||
public static final String ER_ID = "org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem"; //$NON-NLS-1$
|
||||
public static final String PROBLEM_ID = "org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem"; //$NON-NLS-1$
|
||||
|
||||
public void processAst(IASTTranslationUnit ast) {
|
||||
// Traverse the AST using the visitor pattern.
|
||||
ast.accept(new OnEachClass());
|
||||
}
|
||||
|
||||
class OnEachClass extends ASTVisitor {
|
||||
private IASTName className;
|
||||
private IBinding virtualMethod;
|
||||
private IBinding destructor;
|
||||
private IASTDeclSpecifier warningLocation;
|
||||
private static ICPPMethod getDestructor(ICPPClassType classType) {
|
||||
for (ICPPMethod method : classType.getDeclaredMethods()) {
|
||||
if (method.isDestructor()) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean hasVirtualDestructor(ICPPClassType classType) {
|
||||
ICPPMethod destructor = getDestructor(classType);
|
||||
if (destructor != null && destructor.isVirtual()) {
|
||||
return true;
|
||||
}
|
||||
ICPPBase[] bases = classType.getBases();
|
||||
for (ICPPBase base : bases) {
|
||||
IBinding baseClass = base.getBaseClass();
|
||||
if (baseClass instanceof ICPPClassType) {
|
||||
if (hasVirtualDestructor((ICPPClassType) baseClass)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class OnEachClass extends ASTVisitor {
|
||||
OnEachClass() {
|
||||
shouldVisitDeclSpecifiers = true;
|
||||
}
|
||||
|
||||
public int visit(IASTDeclSpecifier decl) {
|
||||
if (isClassDecl(decl)) {
|
||||
try {
|
||||
boolean err = hasErrorCondition(decl);
|
||||
if (err) {
|
||||
String clazz = className.toString();
|
||||
String method = virtualMethod.getName();
|
||||
if (decl instanceof ICPPASTCompositeTypeSpecifier) {
|
||||
ICPPASTCompositeTypeSpecifier spec = (ICPPASTCompositeTypeSpecifier) decl;
|
||||
IASTName className = spec.getName();
|
||||
IBinding binding = className.resolveBinding();
|
||||
if (!(binding instanceof ICPPClassType)) {
|
||||
return PROCESS_SKIP;
|
||||
}
|
||||
ICPPClassType classType = (ICPPClassType) binding;
|
||||
if (hasVirtualDestructor(classType)) {
|
||||
return PROCESS_SKIP;
|
||||
}
|
||||
ICPPMethod virtualMethod = null;
|
||||
for (ICPPMethod method : classType.getAllDeclaredMethods()) {
|
||||
if (!method.isDestructor() && method.isVirtual()) {
|
||||
virtualMethod = method;
|
||||
}
|
||||
}
|
||||
if (virtualMethod == null) {
|
||||
return PROCESS_SKIP;
|
||||
}
|
||||
ICPPMethod destructor = getDestructor(classType);
|
||||
if (destructor != null &&
|
||||
destructor.getVisibility() != ICPPASTVisibilityLabel.v_public &&
|
||||
classType.getFriends().length == 0) {
|
||||
// No error if the destructor is protected or private and there are no friends.
|
||||
return PROCESS_SKIP;
|
||||
}
|
||||
|
||||
IASTNode node = decl;
|
||||
if (destructor != null) {
|
||||
if (destructor instanceof ICPPInternalBinding) {
|
||||
IASTNode[] decls = ((ICPPInternalBinding) destructor).getDeclarations();
|
||||
if (warningLocation == null) {
|
||||
if (decls != null && decls.length > 0)
|
||||
if (decls != null && decls.length > 0) {
|
||||
node = decls[0];
|
||||
} else {
|
||||
node = warningLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
reportProblem(ER_ID, node, clazz, method);
|
||||
}
|
||||
} catch (DOMException e) {
|
||||
// Ignore, not an error.
|
||||
} catch (Exception e) {
|
||||
CodanCheckersActivator.log(e);
|
||||
}
|
||||
reportProblem(PROBLEM_ID, node, className.getSimpleID().toString(),
|
||||
virtualMethod.getName());
|
||||
return PROCESS_SKIP;
|
||||
}
|
||||
return PROCESS_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param decl
|
||||
* @throws DOMException
|
||||
*/
|
||||
private boolean hasErrorCondition(IASTDeclSpecifier decl) throws DOMException {
|
||||
ICPPASTCompositeTypeSpecifier spec = (ICPPASTCompositeTypeSpecifier) decl;
|
||||
className = spec.getName();
|
||||
IBinding binding = className.resolveBinding();
|
||||
if ((binding instanceof ICPPClassType) == false) {
|
||||
return false;
|
||||
}
|
||||
ICPPClassType classType = (ICPPClassType) binding;
|
||||
virtualMethod = null;
|
||||
destructor = null;
|
||||
warningLocation = null;
|
||||
// Check for the following conditions:
|
||||
// class has own virtual method and own non-virtual destructor
|
||||
// class has own virtual method and base non-virtual destructor
|
||||
// class has base virtual method and own non-virtual destructor
|
||||
ICPPMethod[] declaredMethods = classType.getDeclaredMethods();
|
||||
boolean hasOwnVirtualMethod = false;
|
||||
boolean hasOwnNonVirtualDestructor = false; // implicit destructor
|
||||
boolean hasDestructor = false;
|
||||
boolean hasVirtualMethod = false;
|
||||
for (ICPPMethod method : declaredMethods) {
|
||||
if (method.isPureVirtual()) {
|
||||
// Class has at least one pure virtual method, it is abstract and cannot be instantiated.
|
||||
return false;
|
||||
} else if (method.isVirtual() && !method.isDestructor()) {
|
||||
hasOwnVirtualMethod = true;
|
||||
virtualMethod = method;
|
||||
}
|
||||
if (method.isDestructor()) {
|
||||
hasDestructor = true;
|
||||
if (!method.isVirtual()) {
|
||||
hasOwnNonVirtualDestructor = true;
|
||||
destructor = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean hasVirtualDestructor = false;
|
||||
// Class has own virtual method and own non-virtual destructor.
|
||||
if (hasOwnVirtualMethod && hasOwnNonVirtualDestructor) {
|
||||
if (classType.getFriends().length == 0) {
|
||||
if (destructor instanceof ICPPMethod) {
|
||||
// Check visibility of dtor. No error if it is protected or private.
|
||||
if (((ICPPMethod) destructor).getVisibility() != ICPPASTVisibilityLabel.v_public) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Check if one of its base classes has a virtual destructor.
|
||||
return !hasVirtualDtorInBaseClass(classType);
|
||||
}
|
||||
// Destructor can be called from a class declared as friend.
|
||||
return true;
|
||||
}
|
||||
// Class has virtual methods and implicit public destructor.
|
||||
if (hasOwnVirtualMethod && !hasDestructor && classType.getBases().length == 0) {
|
||||
return true;
|
||||
}
|
||||
// Class does not have virtual methods but has virtual destructor
|
||||
// - not an error
|
||||
if (!hasOwnVirtualMethod && hasDestructor && !hasOwnNonVirtualDestructor) {
|
||||
return false;
|
||||
}
|
||||
// Check inherited methods
|
||||
ICPPMethod[] allDeclaredMethods = classType.getAllDeclaredMethods();
|
||||
for (ICPPMethod method : allDeclaredMethods) {
|
||||
if (method.isVirtual() && !method.isDestructor()) {
|
||||
hasVirtualMethod = true;
|
||||
if (virtualMethod == null)
|
||||
virtualMethod = method;
|
||||
}
|
||||
if (method.isDestructor()) {
|
||||
hasDestructor = true;
|
||||
if (method.isVirtual()) {
|
||||
hasVirtualDestructor = true;
|
||||
} else if (destructor == null) {
|
||||
destructor = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasOwnVirtualMethod) {
|
||||
// Class has own virtual method and base non-virtual destructor.
|
||||
if (hasDestructor && !hasVirtualDestructor) {
|
||||
boolean noVirtualDtorInBase = !hasVirtualDtorInBaseClass(classType);
|
||||
if (noVirtualDtorInBase) {
|
||||
warningLocation = decl;
|
||||
}
|
||||
return noVirtualDtorInBase;
|
||||
}
|
||||
} else if (hasVirtualMethod) {
|
||||
// Class has base virtual method and own non-virtual destructor.
|
||||
if (hasOwnNonVirtualDestructor) {
|
||||
return true;
|
||||
}
|
||||
boolean noVirtualDtorInBase = !hasVirtualDtorInBaseClass(classType);
|
||||
if (noVirtualDtorInBase) {
|
||||
warningLocation = decl;
|
||||
}
|
||||
return noVirtualDtorInBase;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasVirtualDtorInBaseClass(ICPPClassType classType) {
|
||||
ICPPBase[] bases = classType.getBases();
|
||||
for (ICPPBase base : bases) {
|
||||
if (!(base.getBaseClass() instanceof ICPPClassType)) {
|
||||
continue;
|
||||
}
|
||||
ICPPClassType testedBaseClass = (ICPPClassType) base.getBaseClass();
|
||||
ICPPMethod[] declaredBaseMethods = testedBaseClass.getDeclaredMethods();
|
||||
for (ICPPMethod method : declaredBaseMethods) {
|
||||
if (method.isPureVirtual()) {
|
||||
return false;
|
||||
}
|
||||
if (method.isDestructor() && method.isVirtual()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (hasVirtualDtorInBaseClass(testedBaseClass))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isClassDecl(IASTDeclSpecifier decl) {
|
||||
return decl instanceof ICPPASTCompositeTypeSpecifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Patrick Hofer - Initial API and implementation
|
||||
* Tomasz Wesolowski
|
||||
* Sergey Prigogin (Google)
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.codan.core.internal.checkers;
|
||||
|
||||
|
@ -14,7 +16,7 @@ import org.eclipse.cdt.codan.core.test.CheckerTestCase;
|
|||
import org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructor;
|
||||
|
||||
/**
|
||||
* Test for {@see NonVirtualDestructor} class.
|
||||
* Test for {@link NonVirtualDestructor} class.
|
||||
*/
|
||||
public class NonVirtualDestructorCheckerTest extends CheckerTestCase {
|
||||
@Override
|
||||
|
@ -25,7 +27,7 @@ public class NonVirtualDestructorCheckerTest extends CheckerTestCase {
|
|||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
enableProblems(NonVirtualDestructor.ER_ID);
|
||||
enableProblems(NonVirtualDestructor.PROBLEM_ID);
|
||||
}
|
||||
|
||||
// struct A {
|
||||
|
@ -86,7 +88,7 @@ public class NonVirtualDestructorCheckerTest extends CheckerTestCase {
|
|||
// struct B {
|
||||
// ~B(); // ok.
|
||||
// };
|
||||
public void testVirtualDtorInBaseClass() {
|
||||
public void testVirtualDtorInBaseClass1() {
|
||||
loadCodeAndRun(getAboveComment());
|
||||
checkNoErrors();
|
||||
}
|
||||
|
@ -108,6 +110,22 @@ public class NonVirtualDestructorCheckerTest extends CheckerTestCase {
|
|||
checkNoErrors();
|
||||
}
|
||||
|
||||
// class A {
|
||||
// public:
|
||||
// virtual ~A();
|
||||
// };
|
||||
//
|
||||
// class B : public A {
|
||||
// public:
|
||||
// ~B();
|
||||
// virtual void m();
|
||||
// friend class C;
|
||||
// };
|
||||
public void testVirtualDtorInBaseClass3() {
|
||||
loadCodeAndRun(getAboveComment());
|
||||
checkNoErrors();
|
||||
}
|
||||
|
||||
// struct A {
|
||||
// virtual void f() { };
|
||||
// ~A(); // warn! public non-virtual dtor.
|
||||
|
@ -122,14 +140,12 @@ public class NonVirtualDestructorCheckerTest extends CheckerTestCase {
|
|||
//
|
||||
// struct E : public D {
|
||||
// };
|
||||
public void testNonVirtualDtorInBaseClass2() {
|
||||
public void testNonVirtualDtorInBaseClass() {
|
||||
loadCodeAndRun(getAboveComment());
|
||||
checkErrorLines(3, 7, 11, 13);
|
||||
}
|
||||
|
||||
// class A { // OK. Do _not_ warn here.
|
||||
// // A is an abstract class because it has one pure virtual method.
|
||||
// // A cannot be instantiated.
|
||||
// class A {
|
||||
// virtual void f1() { };
|
||||
// virtual void f2() = 0;
|
||||
// };
|
||||
|
@ -141,6 +157,28 @@ public class NonVirtualDestructorCheckerTest extends CheckerTestCase {
|
|||
// };
|
||||
public void testAbstractBaseClass() {
|
||||
loadCodeAndRun(getAboveComment());
|
||||
checkNoErrors();
|
||||
// It doesn't matter if the class is abstract or not - dtor can be called polymorphically.
|
||||
checkErrorLines(1);
|
||||
}
|
||||
|
||||
// struct Base {
|
||||
// };
|
||||
// struct Derived : Base {
|
||||
// virtual void bar();
|
||||
// };
|
||||
public void testImplicitDtorInBaseClass() {
|
||||
loadCodeAndRun(getAboveComment());
|
||||
checkErrorLines(3);
|
||||
}
|
||||
|
||||
// struct Base {
|
||||
// ~Base();
|
||||
// };
|
||||
// struct Derived : Base {
|
||||
// virtual void bar();
|
||||
// };
|
||||
public void testExplicitDtorInBaseClass() {
|
||||
loadCodeAndRun(getAboveComment());
|
||||
checkErrorLines(4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
package org.eclipse.cdt.core.settings.model;
|
||||
|
||||
import org.eclipse.cdt.core.settings.model.util.LanguageSettingEntriesSerializer;
|
||||
import org.eclipse.cdt.internal.core.SafeStringInterner;
|
||||
|
||||
|
||||
|
||||
|
@ -19,7 +20,7 @@ public abstract class ACSettingEntry implements ICSettingEntry {
|
|||
String fName;
|
||||
|
||||
ACSettingEntry(String name, int flags){
|
||||
fName = name;
|
||||
fName = SafeStringInterner.safeIntern(name);
|
||||
fFlags = flags;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.cdt.core.settings.model;
|
||||
|
||||
import org.eclipse.cdt.internal.core.SafeStringInterner;
|
||||
|
||||
|
||||
|
||||
public final class CMacroEntry extends ACSettingEntry implements ICMacroEntry{
|
||||
|
@ -17,7 +19,7 @@ public final class CMacroEntry extends ACSettingEntry implements ICMacroEntry{
|
|||
|
||||
public CMacroEntry(String name, String value, int flags) {
|
||||
super(name, flags);
|
||||
fValue = value;
|
||||
fValue = SafeStringInterner.safeIntern(value);
|
||||
if(fValue == null)
|
||||
fValue = ""; //$NON-NLS-1$
|
||||
}
|
||||
|
|
|
@ -105,11 +105,12 @@ public class CPPMethod extends CPPFunction implements ICPPMethod {
|
|||
IASTDeclaration [] members = cls.getMembers();
|
||||
ICPPASTVisibilityLabel vis = null;
|
||||
for (IASTDeclaration member : members) {
|
||||
if( member instanceof ICPPASTVisibilityLabel )
|
||||
if (member instanceof ICPPASTVisibilityLabel) {
|
||||
vis = (ICPPASTVisibilityLabel) member;
|
||||
else if( member == decl )
|
||||
} else if (member == decl) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (vis != null) {
|
||||
return vis.getVisibility();
|
||||
} else if (cls.getKey() == ICPPASTCompositeTypeSpecifier.k_class) {
|
||||
|
@ -156,10 +157,11 @@ public class CPPMethod extends CPPFunction implements ICPPMethod {
|
|||
|
||||
protected ICPPASTDeclSpecifier getDeclSpec(IASTDeclaration decl) {
|
||||
ICPPASTDeclSpecifier declSpec = null;
|
||||
if( decl instanceof IASTSimpleDeclaration )
|
||||
if (decl instanceof IASTSimpleDeclaration) {
|
||||
declSpec = (ICPPASTDeclSpecifier) ((IASTSimpleDeclaration)decl).getDeclSpecifier();
|
||||
else if( decl instanceof IASTFunctionDefinition )
|
||||
} else if (decl instanceof IASTFunctionDefinition) {
|
||||
declSpec = (ICPPASTDeclSpecifier) ((IASTFunctionDefinition)decl).getDeclSpecifier();
|
||||
}
|
||||
return declSpec;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.eclipse.core.resources.IFile;
|
|||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IProjectDescription;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
|
@ -143,7 +144,7 @@ public class StandardExecutableImporter implements IExecutableImporter {
|
|||
for (int i = 0; i < segmentCount; i++) {
|
||||
currentFolder = currentFolder.getFolder(new Path(path.segment(i)));
|
||||
if (!currentFolder.exists()) {
|
||||
((IFolder) currentFolder).create(false, true, new NullProgressMonitor());
|
||||
((IFolder) currentFolder).create(IResource.VIRTUAL | IResource.DERIVED, true, new NullProgressMonitor());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue