|
13 | 13 |
|
14 | 14 | import java
|
15 | 15 |
|
16 |
| -from Method m, MethodCall sysexitCall, Method sysexit, Class system |
17 |
| -where |
18 |
| - sysexitCall = m.getACallSite(sysexit) and |
19 |
| - (sysexit.hasName("exit") or sysexit.hasName("halt")) and |
20 |
| - sysexit.getDeclaringType() = system and |
21 |
| - ( |
22 |
| - system.hasQualifiedName("java.lang", "System") or |
23 |
| - system.hasQualifiedName("java.lang", "Runtime") |
24 |
| - ) and |
25 |
| - m.fromSource() and |
26 |
| - not m instanceof MainMethod |
27 |
| -select sysexitCall, |
28 |
| - "Avoid calls to " + sysexit.getDeclaringType().getName() + "." + sysexit.getName() + |
29 |
| - "() as this makes code harder to reuse." |
| 16 | +/** |
| 17 | + * A `Method` which, when called, causes the JVM to exit or halt. |
| 18 | + * Explicitly includes these methods from the java standard library: |
| 19 | + * - `java.lang.System.exit` |
| 20 | + * - `java.lang.Runtime.halt` |
| 21 | + * - `java.lang.Runtime.exit` |
| 22 | + */ |
| 23 | +class ExitOrHaltMethod extends Method { |
| 24 | + ExitOrHaltMethod() { |
| 25 | + exists(Class system | |
| 26 | + this.getDeclaringType() = system and |
| 27 | + ( |
| 28 | + this.hasName("exit") and |
| 29 | + ( |
| 30 | + system.hasQualifiedName("java.lang", "System") or |
| 31 | + system.hasQualifiedName("java.lang", "Runtime") |
| 32 | + ) |
| 33 | + or |
| 34 | + this.hasName("halt") and |
| 35 | + system.hasQualifiedName("java.lang", "Runtime") |
| 36 | + ) |
| 37 | + ) |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +/** A `MethodCall` to an `ExitOrHaltMethod`, which causes the JVM to exit abruptly. */ |
| 42 | +class ExitOrHaltMethodCall extends MethodCall { |
| 43 | + ExitOrHaltMethodCall() { |
| 44 | + exists(ExitOrHaltMethod exitMethod | this.getMethod() = exitMethod | |
| 45 | + exists(SourceMethodNotMainOrTest srcMethod | this = srcMethod.getACallSite(exitMethod)) |
| 46 | + ) |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +/** |
| 51 | + * Represents an intentional `MethodCall` to a system or runtime "exit" method, such as for |
| 52 | + * functions which exist for the purpose of exiting the program. Assumes that a an exit method |
| 53 | + * call within a method is intentional if the exit code is passed from a parameter of the |
| 54 | + * enclosing method. |
| 55 | + */ |
| 56 | +class IntentionalExitMethodCall extends ExitOrHaltMethodCall { |
| 57 | + IntentionalExitMethodCall() { |
| 58 | + this.getMethod().hasName("exit") and |
| 59 | + this.getAnArgument() = this.getEnclosingCallable().getAParameter().getAnAccess() |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +/** |
| 64 | + * A `Method` that is defined in source code and is not a `MainMethod` or a `LikelyTestMethod`. |
| 65 | + */ |
| 66 | +class SourceMethodNotMainOrTest extends Method { |
| 67 | + SourceMethodNotMainOrTest() { |
| 68 | + this.fromSource() and |
| 69 | + not this instanceof MainMethod and |
| 70 | + not this instanceof LikelyTestMethod and |
| 71 | + not this.getEnclosingCallable() instanceof LikelyTestMethod |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +from ExitOrHaltMethodCall mc |
| 76 | +where not mc instanceof IntentionalExitMethodCall |
| 77 | +select mc, |
| 78 | + "Avoid calls to " + mc.getMethod().getDeclaringType().getName() + "." + mc.getMethod().getName() + |
| 79 | + "() as this prevents runtime cleanup and makes code harder to reuse." |
0 commit comments