View | Details | Raw Unified | Return to bug 368546
Collapse All | Expand All

(-)a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java (-13 / +508 lines)
Lines 26-32 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; Link Here
26
public class ResourceLeakTests extends AbstractRegressionTest {
26
public class ResourceLeakTests extends AbstractRegressionTest {
27
27
28
static {
28
static {
29
//	TESTS_NAMES = new String[] { "testBug368709"};
29
//	TESTS_NAMES = new String[] { "test074"};
30
//	TESTS_NUMBERS = new int[] { 50 };
30
//	TESTS_NUMBERS = new int[] { 50 };
31
//	TESTS_RANGE = new int[] { 11, -1 };
31
//	TESTS_RANGE = new int[] { 11, -1 };
32
}
32
}
Lines 1489-1495 public void test056y() { Link Here
1489
			"        final FileReader reader23 = new FileReader(\"file\");\n" +
1489
			"        final FileReader reader23 = new FileReader(\"file\");\n" +
1490
			"        provider = new ResourceProvider() {\n" +
1490
			"        provider = new ResourceProvider() {\n" +
1491
			"            public FileReader provide() {\n" +
1491
			"            public FileReader provide() {\n" +
1492
			"                return reader23;\n" +
1492
			"                return reader23;\n" + // responsibility now lies at the caller of this method
1493
			"            }\n" +
1493
			"            }\n" +
1494
			"        };\n" +
1494
			"        };\n" +
1495
			"    }\n" +
1495
			"    }\n" +
Lines 1500-1510 public void test056y() { Link Here
1500
		"	final FileReader reader31 = new FileReader(\"file\");\n" +
1500
		"	final FileReader reader31 = new FileReader(\"file\");\n" +
1501
		"	                 ^^^^^^^^\n" +
1501
		"	                 ^^^^^^^^\n" +
1502
		"Potential resource leak: 'reader31' may not be closed\n" +
1502
		"Potential resource leak: 'reader31' may not be closed\n" +
1503
		"----------\n" +
1504
		"2. WARNING in X.java (at line 17)\n" +
1505
		"	final FileReader reader23 = new FileReader(\"file\");\n" +
1506
		"	                 ^^^^^^^^\n" +
1507
		"Potential resource leak: 'reader23' may not be closed\n" +
1508
		"----------\n",
1503
		"----------\n",
1509
		null,
1504
		null,
1510
		true,
1505
		true,
Lines 3143-3154 public void testBug368709a() { Link Here
3143
			"}\n"
3138
			"}\n"
3144
		},
3139
		},
3145
		"----------\n" +
3140
		"----------\n" +
3146
		"1. ERROR in X.java (at line 15)\n" +
3141
		"1. ERROR in X.java (at line 18)\n" +
3147
		"	return wc.open(getObjectId(), type).openStream();\n" +
3148
		"	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
3149
		"Resource leak: \'in\' is not closed at this location\n" +
3150
		"----------\n" +
3151
		"2. ERROR in X.java (at line 18)\n" +
3152
		"	return new ObjectStream.Filter(type, size, in);\n" +
3142
		"	return new ObjectStream.Filter(type, size, in);\n" +
3153
		"	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
3143
		"	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
3154
		"Potential resource leak: \'in\' may not be closed at this location\n" +
3144
		"Potential resource leak: \'in\' may not be closed at this location\n" +
Lines 3189-3192 public void testBug368709b() { Link Here
3189
		true,
3179
		true,
3190
		options);
3180
		options);
3191
}
3181
}
3182
3183
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3184
// example from comment 3
3185
public void test064() {
3186
	Map options = getCompilerOptions();
3187
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3188
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3189
	this.runConformTest(new String[] {
3190
		"Test064.java",
3191
		"import java.io.*;\n" +
3192
		"public class Test064 {\n" +
3193
		"    void foo(File outfile) {\n" + 
3194
		"        OutputStream out= System.out;\n" + 
3195
		"        if (outfile != null) {\n" + 
3196
		"            try {\n" + 
3197
		"                out = new FileOutputStream(outfile);\n" + 
3198
		"            } catch (java.io.IOException e) {\n" + 
3199
		"                throw new RuntimeException(e);\n" + 
3200
		"            }\n" + 
3201
		"        }\n" + 
3202
		"        setOutput(out);\n" + 
3203
		"    }\n" + 
3204
		"    private void setOutput(OutputStream out) { }\n" +
3205
		"}\n"
3206
	},
3207
	"",
3208
	null,
3209
	true,
3210
	null,
3211
	options,
3212
	null);
3213
}
3214
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3215
// example from comment 10
3216
// disabled, because basic null-analysis machinery doesn't support this pattern
3217
// see also Bug 370424 - [compiler][null] throw-catch analysis for null flow could be more precise
3218
public void _test065() {
3219
	Map options = getCompilerOptions();
3220
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3221
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3222
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3223
	this.runConformTest(new String[] {
3224
		"Test065.java",
3225
		"import java.io.*;\n" +
3226
		"class MyException extends Exception{}\n" + 
3227
		"public class Test065 {\n" +
3228
		"	void foo(String fileName) throws IOException, MyException {\n" + 
3229
		"		FileReader       fileRead   = new FileReader(fileName);\n" + 
3230
		"		BufferedReader   bufRead    = new BufferedReader(fileRead);\n" + 
3231
		"		LineNumberReader lineReader = new LineNumberReader(bufRead);\n" + 
3232
		"		try {\n" + 
3233
		"		while (lineReader.readLine() != null) {\n" + 
3234
		"			bufRead.close();\n" + 
3235
		"			callSome();  // only this can throw MyException\n" + 
3236
		"		}\n" + 
3237
		"		} catch (MyException e) {\n" + 
3238
		"			throw e;  // Pot. leak reported here\n" + 
3239
		"		}\n" + 
3240
		"		bufRead.close(); \n" + 
3241
		"	}\n" + 
3242
		"	private void callSome() throws MyException\n" + 
3243
		"	{\n" + 
3244
		"		\n" + 
3245
		"	}\n" + 
3246
		"}\n"
3247
	},
3248
	"",
3249
	null,
3250
	true,
3251
	null,
3252
	options,
3253
	null);
3254
}
3255
3256
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3257
// example from comment 11
3258
public void test066() {
3259
	Map options = getCompilerOptions();
3260
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3261
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3262
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3263
	this.runNegativeTest(new String[] {
3264
		"Test066.java",
3265
		"import java.io.*;\n" +
3266
		"class MyException extends Exception{}\n" + 
3267
		"public class Test066 {\n" +
3268
		"    void countFileLines(String fileName) throws IOException {\n" + 
3269
		"		FileReader       fileRead   = new FileReader(fileName);\n" + 
3270
		"		BufferedReader   bufRead    = new BufferedReader(fileRead);\n" + 
3271
		"		LineNumberReader lineReader = new LineNumberReader(bufRead);\n" + 
3272
		"		while (lineReader.readLine() != null) {\n" + 
3273
		"			if (lineReader.markSupported())\n" +
3274
		"               throw new IOException();\n" + 
3275
		"			bufRead.close();\n" + 
3276
		"		}\n" + 
3277
		"		bufRead.close();\n" + 
3278
		"	}\n" + 
3279
		"}\n"
3280
	},
3281
	"----------\n" + 
3282
	"1. ERROR in Test066.java (at line 10)\n" + 
3283
	"	throw new IOException();\n" + 
3284
	"	^^^^^^^^^^^^^^^^^^^^^^^^\n" + 
3285
	"Resource leak: \'lineReader\' is not closed at this location\n" + 
3286
	"----------\n",
3287
	null,
3288
	true,
3289
	options);
3290
}
3291
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3292
// example from comment 11 - variant with closing top-level resource 
3293
public void test066b() {
3294
	Map options = getCompilerOptions();
3295
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3296
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3297
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3298
	this.runNegativeTest(new String[] {
3299
		"Test066.java",
3300
		"import java.io.*;\n" +
3301
		"class MyException extends Exception{}\n" + 
3302
		"public class Test066 {\n" +
3303
		"    void countFileLines(String fileName) throws IOException {\n" + 
3304
		"		FileReader       fileRead   = new FileReader(fileName);\n" + 
3305
		"		BufferedReader   bufRead    = new BufferedReader(fileRead);\n" + 
3306
		"		LineNumberReader lineReader = new LineNumberReader(bufRead);\n" + 
3307
		"		while (lineReader.readLine() != null) {\n" + 
3308
		"			if (lineReader.markSupported())\n" +
3309
		"               throw new IOException();\n" + 
3310
		"			lineReader.close();\n" + 
3311
		"		}\n" + 
3312
		"		lineReader.close();\n" + 
3313
		"	}\n" + 
3314
		"}\n"
3315
	},
3316
	"----------\n" + 
3317
	"1. ERROR in Test066.java (at line 10)\n" + 
3318
	"	throw new IOException();\n" + 
3319
	"	^^^^^^^^^^^^^^^^^^^^^^^^\n" + 
3320
	"Potential resource leak: \'lineReader\' may not be closed at this location\n" + 
3321
	"----------\n",
3322
	null,
3323
	true,
3324
	options);
3325
}
3326
3327
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3328
// example from comment 12
3329
// disabled because null info after try-catch is too weak,
3330
// see also Bug 370424 - [compiler][null] throw-catch analysis for null flow could be more precise
3331
public void _test067() {
3332
	Map options = getCompilerOptions();
3333
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3334
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3335
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3336
	this.runConformTest(new String[] {
3337
		"Test067.java",
3338
		"import java.io.*;\n" +
3339
		"public class Test067 {\n" +
3340
		"	public void comment12() throws IOException {\n" + 
3341
		"    	LineNumberReader o = null;\n" + 
3342
		"    	try {\n" + 
3343
		"    		o = new LineNumberReader(null);    		\n" + 
3344
		"    	} catch (NumberFormatException e) {    		\n" + 
3345
		"    	}\n" + 
3346
		"    }\n" + 
3347
		"}\n"
3348
	},
3349
	"",
3350
	null,
3351
	true,
3352
	null,
3353
	options,
3354
	null);
3355
}
3356
3357
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3358
// example from comment 12
3359
// disabled because null info after try-catch is too weak,
3360
// see also Bug 370424 - [compiler][null] throw-catch analysis for null flow could be more precise
3361
public void _test067b() {
3362
	Map options = getCompilerOptions();
3363
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3364
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3365
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3366
	this.runConformTest(new String[] {
3367
		"Test067.java",
3368
		"import java.io.*;\n" +
3369
		"public class Test067 {\n" +
3370
		"	public void comment12b() throws IOException {\n" + 
3371
		"		LineNumberReader o = new LineNumberReader(null);\n" + 
3372
		"    	try {\n" + 
3373
		"    		o.close();\n" + 
3374
		"    	} catch (NumberFormatException e) {\n" + 
3375
		"    	}\n" + 
3376
		"    }\n" + 
3377
		"}\n"
3378
	},
3379
	"",
3380
	null,
3381
	true,
3382
	null,
3383
	options,
3384
	null);
3385
}
3386
3387
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3388
// example from comment 13
3389
public void test068() {
3390
	Map options = getCompilerOptions();
3391
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3392
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3393
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3394
	this.runConformTest(new String[] {
3395
		"Test068.java",
3396
		"import java.io.*;\n" +
3397
		"public class Test068 {\n" +
3398
		"	class ProcessingStep extends OutputStream {\n" + 
3399
		"		public void write(int b) throws IOException {}\n" + 
3400
		"		public OutputStream getDestination() { return null; }\n" + 
3401
		"	}\n" + 
3402
		"	class ArtifactOutputStream  extends OutputStream {\n" + 
3403
		"		public void write(int b) throws IOException {}\n" + 
3404
		"	}" +
3405
		"	ArtifactOutputStream comment13(OutputStream stream) {\n" + 
3406
		"		OutputStream current = stream;\n" + 
3407
		"		while (current instanceof ProcessingStep)\n" + 
3408
		"			current = ((ProcessingStep) current).getDestination(); //warning\n" + 
3409
		"		if (current instanceof ArtifactOutputStream)\n" + 
3410
		"			return (ArtifactOutputStream) current;\n" + 
3411
		"		return null;\n" + 
3412
		"	}\n" + 
3413
		"}\n"
3414
	},
3415
	"",
3416
	null,
3417
	true,
3418
	null,
3419
	options,
3420
	null);
3421
}
3422
3423
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3424
// example from comment 16
3425
public void test069() {
3426
	if (this.complianceLevel < ClassFileConstants.JDK1_5) return; // generics used
3427
	Map options = getCompilerOptions();
3428
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3429
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3430
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3431
	this.runConformTest(new String[] {
3432
		"Test069.java",
3433
		"import java.io.*;\n" +
3434
		"import java.util.Collection;\n" +
3435
		"public class Test069 {\n" +
3436
		"	class Profile {}\n" + 
3437
		"	class CoreException extends Exception {}\n" + 
3438
		"	void writeProfilesToStream(Collection<Profile> p, OutputStream s, String enc) {}\n" + 
3439
		"	CoreException createException(IOException ioex, String message) { return new CoreException(); }\n" + 
3440
		"	public void comment16(Collection<Profile> profiles, File file, String encoding) throws CoreException {\n" + 
3441
		"		final OutputStream stream;\n" + 
3442
		"		try {\n" + 
3443
		"			stream= new FileOutputStream(file);\n" + 
3444
		"			try {\n" + 
3445
		"				writeProfilesToStream(profiles, stream, encoding);\n" + 
3446
		"			} finally {\n" + 
3447
		"				try { stream.close(); } catch (IOException e) { /* ignore */ }\n" + 
3448
		"			}\n" + 
3449
		"		} catch (IOException e) {\n" + 
3450
		"			throw createException(e, \"message\"); // should not shout here\n" + 
3451
		"		}\n" + 
3452
		"	}\n" + 
3453
		"}\n"
3454
	},
3455
	"",
3456
	null,
3457
	true,
3458
	null,
3459
	options,
3460
	null);
3461
}
3462
3463
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3464
// referenced in array initializer 
3465
public void test070() {
3466
	Map options = getCompilerOptions();
3467
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3468
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3469
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3470
	this.runNegativeTest(new String[] {
3471
		"Test070.java",
3472
		"import java.io.*;\n" +
3473
		"public class Test070 {\n" +
3474
		"    void storeInArray(String fileName) throws IOException {\n" + 
3475
		"		FileReader       fileRead   = new FileReader(fileName);\n" + 
3476
		"		closeThemAll(new FileReader[] { fileRead });\n" + 
3477
		"	}\n" +
3478
		"   void closeThemAll(FileReader[] readers) { }\n" + 
3479
		"}\n"
3480
	},
3481
	"----------\n" + 
3482
	"1. ERROR in Test070.java (at line 4)\n" + 
3483
	"	FileReader       fileRead   = new FileReader(fileName);\n" + 
3484
	"	                 ^^^^^^^^\n" + 
3485
	"Potential resource leak: \'fileRead\' may not be closed\n" + 
3486
	"----------\n",
3487
	null,
3488
	true,
3489
	options);
3490
}
3491
3492
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3493
// referenced in array initializer 
3494
public void test071() {
3495
	Map options = getCompilerOptions();
3496
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3497
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3498
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3499
	this.runNegativeTest(new String[] {
3500
		"Test071.java",
3501
		"import java.io.*;\n" +
3502
		"public class Test071 {\n" +
3503
		"    class ReaderHolder {\n" + 
3504
		"		FileReader reader;\n" + 
3505
		"	}\n" + 
3506
		"	private FileReader getReader() {\n" + 
3507
		"		return null;\n" + 
3508
		"	}\n" + 
3509
		"	void invokeCompiler(ReaderHolder readerHolder, boolean flag) throws FileNotFoundException {\n" + 
3510
		"		FileReader reader = readerHolder.reader;\n" + 
3511
		"		if (reader == null)\n" + 
3512
		"			reader = getReader();\n" + 
3513
		"		try {\n" + 
3514
		"			return;\n" + 
3515
		"		} finally {\n" + 
3516
		"			try {\n" + 
3517
		"				if (flag)\n" + 
3518
		"					reader.close();\n" + 
3519
		"			} catch (IOException e) {\n" + 
3520
		"				// nop\n" + 
3521
		"			}\n" + 
3522
		"		}\n" + 
3523
		"	}\n" + 
3524
		"}\n"
3525
	},
3526
	"----------\n" + 
3527
	"1. ERROR in Test071.java (at line 14)\n" + 
3528
	"	return;\n" + 
3529
	"	^^^^^^^\n" + 
3530
	"Potential resource leak: \'reader\' may not be closed at this location\n" + 
3531
	"----------\n",
3532
	null,
3533
	true,
3534
	options);
3535
}
3536
3537
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3538
// referenced in array initializer
3539
// disabled because it would require correlation analysis between the tracking variable and its original
3540
// need to pass to downstream: either (nonnull & open) or (null)
3541
public void _test071b() {
3542
	Map options = getCompilerOptions();
3543
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3544
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3545
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3546
	this.runNegativeTest(new String[] {
3547
		"Test071b.java",
3548
		"import java.io.*;\n" +
3549
		"public class Test071b {\n" +
3550
		"   private FileReader getReader() {\n" + 
3551
		"		return null;\n" + 
3552
		"	}\n" + 
3553
		"	void invokeCompiler(boolean flag) throws FileNotFoundException {\n" + 
3554
		"		FileReader reader = null;\n" + 
3555
		"		if (flag)\n" + 
3556
		"			reader = new FileReader(\"file\");\n" + 
3557
		"		if (reader == null)\n" + 
3558
		"			reader = getReader();\n" + 
3559
		"		try {\n" + 
3560
		"			return;\n" + 
3561
		"		} finally {\n" + 
3562
		"			try {\n" + 
3563
		"				if (flag)\n" + 
3564
		"					reader.close();\n" + 
3565
		"			} catch (IOException e) {\n" + 
3566
		"				// nop\n" + 
3567
		"			}\n" + 
3568
		"		}\n" + 
3569
		"	}\n" + 
3570
		"}\n"
3571
	},
3572
	"----------\n" + 
3573
	"1. ERROR in Test071.java (at line 14)\n" + 
3574
	"	return;\n" + 
3575
	"	^^^^^^^\n" + 
3576
	"Potential resource leak: \'reader\' may not be closed at this location\n" + 
3577
	"----------\n",
3578
	null,
3579
	true,
3580
	options);
3581
}
3582
3583
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3584
// throw inside loop inside try - while closed in finally
3585
public void test072() {
3586
	Map options = getCompilerOptions();
3587
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3588
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3589
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3590
	this.runConformTest(new String[] {
3591
		"Test072.java",
3592
		"import java.io.*;\n" +
3593
		"public class Test072 {\n" +
3594
		"   void readState(File file) {\n" + 
3595
		"		DataInputStream in = null;\n" + 
3596
		"		try {\n" + 
3597
		"			in= new DataInputStream(new BufferedInputStream(new FileInputStream(file)));\n" + 
3598
		"			int sizeOfFlags = in.readInt();\n" + 
3599
		"			for (int i = 0; i < sizeOfFlags; ++i) {\n" + 
3600
		"				String childPath = in.readUTF();\n" + 
3601
		"				if (childPath.length() == 0)\n" + 
3602
		"					throw new IOException();\n" + 
3603
		"			}\n" + 
3604
		"		}\n" + 
3605
		"		catch (IOException ioe) { /* nop */ }\n" + 
3606
		"		finally {\n" + 
3607
		"			if (in != null) {\n" + 
3608
		"				try {in.close();} catch (IOException ioe) {}\n" + 
3609
		"			}\n" + 
3610
		"		}\n" + 
3611
		"	}\n" + 
3612
		"}\n"
3613
	},
3614
	"",
3615
	null,
3616
	true,
3617
	null,
3618
	options,
3619
	null);
3620
}
3621
3622
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3623
// unspecific parameter is casted into a resource, yet need to mark as OWNED_BY_OUTSIDE
3624
public void test073() {
3625
	Map options = getCompilerOptions();
3626
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3627
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3628
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3629
	this.runConformTest(new String[] {
3630
		"Test073.java",
3631
		"import java.io.*;\n" +
3632
		"public class Test073 {\n" +
3633
		"   String getEncoding(Object reader) {\n" + 
3634
		"		if (reader instanceof FileReader) {\n" + 
3635
		"			final FileReader fr = (FileReader) reader;\n" + 
3636
		"			return fr.getEncoding();\n" + 
3637
		"		}\n" + 
3638
		"		return null;\n" + 
3639
		"	}\n" + 
3640
		"}\n"
3641
	},
3642
	"",
3643
	null,
3644
	true,
3645
	null,
3646
	options,
3647
	null);
3648
}
3649
3650
// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
3651
// status after nested try-finally
3652
public void test074() {
3653
	Map options = getCompilerOptions();
3654
	options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
3655
	options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
3656
	options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE);
3657
	this.runNegativeTest(new String[] {
3658
		"Test074.java",
3659
		"import java.io.*;\n" +
3660
		"public class Test074 {\n" +
3661
		"   void foo() throws FileNotFoundException {\n" + 
3662
		"		FileOutputStream out = null;\n" + 
3663
		"		try {\n" + 
3664
		"			out = new FileOutputStream(\"outfile\");\n" + 
3665
		"		} finally {\n" + 
3666
		"			try {\n" + 
3667
		"				out.flush();\n" + 
3668
		"				out.close();\n" + 
3669
		"			} catch (IOException e) {\n" + 
3670
		"				e.printStackTrace();\n" + 
3671
		"			}\n" + 
3672
		"			out = null;\n" + // unclosed if exception occurred on flush()
3673
		"		}\n" + 
3674
		"	}\n" + 
3675
		"}\n"
3676
	},
3677
	"----------\n" + 
3678
	"1. ERROR in Test074.java (at line 14)\n" + 
3679
	"	out = null;\n" + 
3680
	"	^^^^^^^^^^\n" + 
3681
	"Potential resource leak: \'out\' may not be closed at this location\n" + 
3682
	"----------\n",
3683
	null,
3684
	true,
3685
	options);
3686
}
3192
}
3687
}
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java (-3 / +5 lines)
Lines 64-72 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl Link Here
64
		analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments);
64
		analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments);
65
	}
65
	}
66
66
67
	if (FakedTrackingVariable.isAnyCloseable(this.resolvedType))
68
		FakedTrackingVariable.analyseCloseableAllocation(currentScope, flowInfo, this);
69
70
	// record some dependency information for exception types
67
	// record some dependency information for exception types
71
	ReferenceBinding[] thrownExceptions;
68
	ReferenceBinding[] thrownExceptions;
72
	if (((thrownExceptions = this.binding.thrownExceptions).length) != 0) {
69
	if (((thrownExceptions = this.binding.thrownExceptions).length) != 0) {
Lines 81-86 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl Link Here
81
			flowInfo.unconditionalCopy(),
78
			flowInfo.unconditionalCopy(),
82
			currentScope);
79
			currentScope);
83
	}
80
	}
81
82
	// after having analysed exceptions above start tracking newly allocated resource:
83
	if (FakedTrackingVariable.isAnyCloseable(this.resolvedType))
84
		FakedTrackingVariable.analyseCloseableAllocation(currentScope, flowInfo, this);
85
84
	if (this.binding.declaringClass.isMemberType() && !this.binding.declaringClass.isStatic()) {
86
	if (this.binding.declaringClass.isMemberType() && !this.binding.declaringClass.isStatic()) {
85
		// allocating a non-static member type without an enclosing instance of parent type
87
		// allocating a non-static member type without an enclosing instance of parent type
86
		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=335845
88
		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=335845
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayInitializer.java (-1 / +5 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2009 IBM Corporation and others.
2
 * Copyright (c) 2000, 2012 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 34-39 public class ArrayInitializer extends Expression { Link Here
34
		if (this.expressions != null) {
34
		if (this.expressions != null) {
35
			for (int i = 0, max = this.expressions.length; i < max; i++) {
35
			for (int i = 0, max = this.expressions.length; i < max; i++) {
36
				flowInfo = this.expressions[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
36
				flowInfo = this.expressions[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
37
38
				if (FakedTrackingVariable.isAnyCloseable(this.expressions[i].resolvedType)) {
39
					flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.expressions[i], flowInfo, false);
40
				}
37
			}
41
			}
38
		}
42
		}
39
		return flowInfo;
43
		return flowInfo;
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java (-1 / +1 lines)
Lines 39-45 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl Link Here
39
	}
39
	}
40
40
41
	if (this.explicitDeclarations > 0) // if block has its own scope analyze tracking vars now:
41
	if (this.explicitDeclarations > 0) // if block has its own scope analyze tracking vars now:
42
		this.scope.checkUnclosedCloseables(flowInfo, null, null);
42
		this.scope.checkUnclosedCloseables(flowInfo, flowContext, null, null);
43
	return flowInfo;
43
	return flowInfo;
44
}
44
}
45
/**
45
/**
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java (-1 / +1 lines)
Lines 177-183 public void analyseCode(ClassScope classScope, InitializationFlowContext initial Link Here
177
		constructorContext.complainIfUnusedExceptionHandlers(this);
177
		constructorContext.complainIfUnusedExceptionHandlers(this);
178
		// check unused parameters
178
		// check unused parameters
179
		this.scope.checkUnusedParameters(this.binding);
179
		this.scope.checkUnusedParameters(this.binding);
180
		this.scope.checkUnclosedCloseables(flowInfo, null/*don't report against a specific location*/, null);
180
		this.scope.checkUnclosedCloseables(flowInfo, null, null/*don't report against a specific location*/, null);
181
	} catch (AbortMethod e) {
181
	} catch (AbortMethod e) {
182
		this.ignoreFurtherInvestigation = true;
182
		this.ignoreFurtherInvestigation = true;
183
	}
183
	}
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java (-6 / +55 lines)
Lines 109-114 public class FakedTrackingVariable extends LocalDeclaration { Link Here
109
				scope.getJavaLangObject(),  // dummy, just needs to be a reference type
109
				scope.getJavaLangObject(),  // dummy, just needs to be a reference type
110
				0,
110
				0,
111
				false);
111
				false);
112
		this.binding.closeTracker = this;
112
		this.binding.declaringScope = scope;
113
		this.binding.declaringScope = scope;
113
		this.binding.setConstant(Constant.NotAConstant);
114
		this.binding.setConstant(Constant.NotAConstant);
114
		this.binding.useFlag = LocalVariableBinding.USED;
115
		this.binding.useFlag = LocalVariableBinding.USED;
Lines 341-349 public class FakedTrackingVariable extends LocalDeclaration { Link Here
341
			FakedTrackingVariable rhsTrackVar = getCloseTrackingVariable(rhs);
342
			FakedTrackingVariable rhsTrackVar = getCloseTrackingVariable(rhs);
342
			if (rhsTrackVar != null) {								// 1. if RHS has a tracking variable...
343
			if (rhsTrackVar != null) {								// 1. if RHS has a tracking variable...
343
				if (local.closeTracker == null) {
344
				if (local.closeTracker == null) {
344
					// null shouldn't occur but let's play safe
345
					// null shouldn't occur but let's play safe:
345
					if (rhsTrackVar.originalBinding != null)
346
					if (rhsTrackVar.originalBinding != null)
346
						local.closeTracker = rhsTrackVar;			//		a.: let fresh LHS share it 
347
						local.closeTracker = rhsTrackVar;			//		a.: let fresh LHS share it
348
					if (rhsTrackVar.currentAssignment == location) {
349
						// pre-set tracker from lhs - passed from outside?
350
						// now it's a fresh resource
351
						rhsTrackVar.globalClosingState &= ~(SHARED_WITH_OUTSIDE|OWNED_BY_OUTSIDE);
352
					}
347
				} else {
353
				} else {
348
					if (rhsTrackVar == disconnectedTracker && rhs instanceof AllocationExpression)
354
					if (rhsTrackVar == disconnectedTracker && rhs instanceof AllocationExpression)
349
						return; 									// 		b.: self wrapper: res = new Wrap(res); -> done!
355
						return; 									// 		b.: self wrapper: res = new Wrap(res); -> done!
Lines 420-427 public class FakedTrackingVariable extends LocalDeclaration { Link Here
420
			FakedTrackingVariable tracker = new FakedTrackingVariable(local, location);
426
			FakedTrackingVariable tracker = new FakedTrackingVariable(local, location);
421
			tracker.globalClosingState |= SHARED_WITH_OUTSIDE;
427
			tracker.globalClosingState |= SHARED_WITH_OUTSIDE;
422
			flowInfo.markPotentiallyNullBit(tracker.binding); // shed some doubt
428
			flowInfo.markPotentiallyNullBit(tracker.binding); // shed some doubt
423
			return tracker;			
429
			return tracker;
424
		} else if ((expression.bits & RestrictiveFlagMASK) == Binding.FIELD) 
430
		} else if (
431
				(expression.bits & RestrictiveFlagMASK) == Binding.FIELD
432
				||((expression instanceof QualifiedNameReference)
433
						&& ((QualifiedNameReference) expression).isFieldAccess()))
425
		{
434
		{
426
			// responsibility for this resource probably lies at a higher level
435
			// responsibility for this resource probably lies at a higher level
427
			FakedTrackingVariable tracker = new FakedTrackingVariable(local, location);
436
			FakedTrackingVariable tracker = new FakedTrackingVariable(local, location);
Lines 440-446 public class FakedTrackingVariable extends LocalDeclaration { Link Here
440
		if (local.closeTracker != null)
449
		if (local.closeTracker != null)
441
			// (c): inner has already been analyzed: -> re-use track var
450
			// (c): inner has already been analyzed: -> re-use track var
442
			return local.closeTracker;
451
			return local.closeTracker;
443
		return new FakedTrackingVariable(local, location);
452
		FakedTrackingVariable newTracker = new FakedTrackingVariable(local, location);
453
		LocalVariableBinding rhsLocal = expression.localVariableBinding();
454
		if (rhsLocal != null && rhsLocal.isParameter()) {
455
			newTracker.globalClosingState |= OWNED_BY_OUTSIDE;
456
		}
457
		return newTracker;
444
	}
458
	}
445
459
446
	public static void cleanUpAfterAssignment(BlockScope currentScope, int lhsBits, Expression expression) {
460
	public static void cleanUpAfterAssignment(BlockScope currentScope, int lhsBits, Expression expression) {
Lines 631-636 public class FakedTrackingVariable extends LocalDeclaration { Link Here
631
		return trackingVar;
645
		return trackingVar;
632
	}
646
	}
633
647
648
	/**
649
	 * Answer true if we know for sure that no resource is bound to this variable
650
	 * at the point of 'flowInfo'. 
651
	 */
652
	public boolean hasDefinitelyNoResource(FlowInfo flowInfo) {
653
		if (this.originalBinding == null) return false; // shouldn't happen but keep quiet.
654
		if (flowInfo.isDefinitelyNull(this.originalBinding)) {
655
			return true;
656
		}
657
		if (!(flowInfo.isDefinitelyAssigned(this.originalBinding) 
658
				|| flowInfo.isPotentiallyAssigned(this.originalBinding))) {
659
			return true;
660
		}
661
		return false;
662
	}
663
664
	public boolean isClosedInFinallyOfEnclosing(BlockScope scope) {
665
		BlockScope currentScope = scope;
666
		while (true) {			
667
			if (currentScope.finallyInfo != null
668
					&& currentScope.finallyInfo.isDefinitelyNonNull(this.binding)) {
669
				return true; // closed in enclosing finally
670
			}
671
			if (!(currentScope.parent instanceof BlockScope)) {
672
				return false;
673
			}
674
			currentScope = (BlockScope) currentScope.parent;
675
		} 
676
	}
634
	/** 
677
	/** 
635
	 * If current is the same as 'returnedResource' or a wrapper thereof,
678
	 * If current is the same as 'returnedResource' or a wrapper thereof,
636
	 * mark as reported and return true, otherwise false.
679
	 * mark as reported and return true, otherwise false.
Lines 648-653 public class FakedTrackingVariable extends LocalDeclaration { Link Here
648
	}
691
	}
649
692
650
	public void recordErrorLocation(ASTNode location, int nullStatus) {
693
	public void recordErrorLocation(ASTNode location, int nullStatus) {
694
		if ((this.globalClosingState & OWNED_BY_OUTSIDE) != 0) {
695
			return;
696
		}
651
		if (this.recordedLocations == null)
697
		if (this.recordedLocations == null)
652
			this.recordedLocations = new HashMap();
698
			this.recordedLocations = new HashMap();
653
		this.recordedLocations.put(location, new Integer(nullStatus));
699
		this.recordedLocations.put(location, new Integer(nullStatus));
Lines 684-695 public class FakedTrackingVariable extends LocalDeclaration { Link Here
684
	}
730
	}
685
	
731
	
686
	public int reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus) {
732
	public int reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus) {
733
		if ((this.globalClosingState & OWNED_BY_OUTSIDE) != 0) {
734
			return 0; // TODO: should we still propagate some flags??
735
		}
687
		// which degree of problem?
736
		// which degree of problem?
688
		boolean isPotentialProblem = false;
737
		boolean isPotentialProblem = false;
689
		if (nullStatus == FlowInfo.NULL) {
738
		if (nullStatus == FlowInfo.NULL) {
690
			if ((this.globalClosingState & CLOSED_IN_NESTED_METHOD) != 0)
739
			if ((this.globalClosingState & CLOSED_IN_NESTED_METHOD) != 0)
691
				isPotentialProblem = true;
740
				isPotentialProblem = true;
692
		} else if (nullStatus == FlowInfo.POTENTIALLY_NULL) {
741
		} else if ((nullStatus & (FlowInfo.POTENTIALLY_NULL|FlowInfo.POTENTIALLY_NON_NULL)) != 0) {
693
			isPotentialProblem = true;
742
			isPotentialProblem = true;
694
		}
743
		}
695
		// report:
744
		// report:
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java (-2 / +2 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2011 IBM Corporation and others.
2
 * Copyright (c) 2000, 2012 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 139-145 public class MethodDeclaration extends AbstractMethodDeclaration { Link Here
139
				}
139
				}
140
					
140
					
141
			}
141
			}
142
			this.scope.checkUnclosedCloseables(flowInfo, null/*don't report against a specific location*/, null);
142
			this.scope.checkUnclosedCloseables(flowInfo, null, null/*don't report against a specific location*/, null);
143
		} catch (AbortMethod e) {
143
		} catch (AbortMethod e) {
144
			this.ignoreFurtherInvestigation = true;
144
			this.ignoreFurtherInvestigation = true;
145
		}
145
		}
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java (-1 / +7 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2011 IBM Corporation and others.
2
 * Copyright (c) 2000, 2012 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 109-114 public class QualifiedAllocationExpression extends AllocationExpression { Link Here
109
				flowInfo.unconditionalCopy(),
109
				flowInfo.unconditionalCopy(),
110
				currentScope);
110
				currentScope);
111
		}
111
		}
112
113
		// after having analysed exceptions above start tracking newly allocated resource:
114
		if (FakedTrackingVariable.isAnyCloseable(this.resolvedType)) {
115
			FakedTrackingVariable.analyseCloseableAllocation(currentScope, flowInfo, this);
116
		}
117
112
		manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
118
		manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
113
		manageSyntheticAccessIfNecessary(currentScope, flowInfo);
119
		manageSyntheticAccessIfNecessary(currentScope, flowInfo);
114
		return flowInfo;
120
		return flowInfo;
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java (+7 lines)
Lines 785-790 public TypeBinding getOtherFieldBindings(BlockScope scope) { Link Here
785
			: type;
785
			: type;
786
}
786
}
787
787
788
public boolean isFieldAccess() {
789
	if (this.otherBindings != null) {
790
		return true;
791
	}
792
	return (this.bits & ASTNode.RestrictiveFlagMASK) == Binding.FIELD;
793
}
794
788
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
795
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
789
	//If inlinable field, forget the access emulation, the code gen will directly target it
796
	//If inlinable field, forget the access emulation, the code gen will directly target it
790
	if (((this.bits & ASTNode.DepthMASK) == 0) || (this.constant != Constant.NotAConstant)) {
797
	if (((this.bits & ASTNode.DepthMASK) == 0) || (this.constant != Constant.NotAConstant)) {
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java (-1 / +1 lines)
Lines 129-135 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl Link Here
129
			}
129
			}
130
		}
130
		}
131
	}
131
	}
132
	currentScope.checkUnclosedCloseables(flowInfo, this, currentScope);
132
	currentScope.checkUnclosedCloseables(flowInfo, flowContext, this, currentScope);
133
	return FlowInfo.DEAD_END;
133
	return FlowInfo.DEAD_END;
134
}
134
}
135
void checkAgainstNullAnnotation(BlockScope scope, FlowContext flowContext, int nullStatus) {
135
void checkAgainstNullAnnotation(BlockScope scope, FlowContext flowContext, int nullStatus) {
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java (-3 / +3 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2011 IBM Corporation and others.
2
 * Copyright (c) 2000, 2012 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 126-139 public int complainIfUnreachable(FlowInfo flowInfo, BlockScope scope, int previo Link Here
126
			if (previousComplaintLevel < COMPLAINED_UNREACHABLE) {
126
			if (previousComplaintLevel < COMPLAINED_UNREACHABLE) {
127
				scope.problemReporter().unreachableCode(this);
127
				scope.problemReporter().unreachableCode(this);
128
				if (endOfBlock)
128
				if (endOfBlock)
129
					scope.checkUnclosedCloseables(flowInfo, null, null);
129
					scope.checkUnclosedCloseables(flowInfo, null, null, null);
130
			}
130
			}
131
			return COMPLAINED_UNREACHABLE;
131
			return COMPLAINED_UNREACHABLE;
132
		} else {
132
		} else {
133
			if (previousComplaintLevel < COMPLAINED_FAKE_REACHABLE) {
133
			if (previousComplaintLevel < COMPLAINED_FAKE_REACHABLE) {
134
				scope.problemReporter().fakeReachable(this);
134
				scope.problemReporter().fakeReachable(this);
135
				if (endOfBlock)
135
				if (endOfBlock)
136
					scope.checkUnclosedCloseables(flowInfo, null, null);
136
					scope.checkUnclosedCloseables(flowInfo, null, null, null);
137
			}
137
			}
138
			return COMPLAINED_FAKE_REACHABLE;
138
			return COMPLAINED_FAKE_REACHABLE;
139
		}
139
		}
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThrowStatement.java (-2 / +2 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2011 IBM Corporation and others.
2
 * Copyright (c) 2000, 2012 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 36-42 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl Link Here
36
	this.exception.checkNPE(currentScope, flowContext, flowInfo);
36
	this.exception.checkNPE(currentScope, flowContext, flowInfo);
37
	// need to check that exception thrown is actually caught somewhere
37
	// need to check that exception thrown is actually caught somewhere
38
	flowContext.checkExceptionHandlers(this.exceptionType, this, flowInfo, currentScope);
38
	flowContext.checkExceptionHandlers(this.exceptionType, this, flowInfo, currentScope);
39
	currentScope.checkUnclosedCloseables(flowInfo, this, currentScope);
39
	currentScope.checkUnclosedCloseables(flowInfo, flowContext, this, currentScope);
40
	return FlowInfo.DEAD_END;
40
	return FlowInfo.DEAD_END;
41
}
41
}
42
42
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java (-16 / +21 lines)
Lines 34-41 public class FinallyFlowContext extends FlowContext { Link Here
34
	VariableBinding[] finalVariables;
34
	VariableBinding[] finalVariables;
35
	int assignCount;
35
	int assignCount;
36
36
37
	// the following three arrays are in sync regarding their indices:
37
	VariableBinding[] nullVariables;
38
	VariableBinding[] nullVariables;
38
	Expression[] nullReferences;
39
	ASTNode[] nullReferences;	// Expressions for null checking, Statements for resource analysis
40
								// cast to Expression is safe if corresponding nullCheckType != EXIT_RESOURCE
39
	int[] nullCheckTypes;
41
	int[] nullCheckTypes;
40
	int nullCount;
42
	int nullCount;
41
	// see also the related field FlowContext#expectedTypes
43
	// see also the related field FlowContext#expectedTypes
Lines 89-95 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
89
	if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative
91
	if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative
90
		for (int i = 0; i < this.nullCount; i++) {
92
		for (int i = 0; i < this.nullCount; i++) {
91
			if (this.nullCheckTypes[i] == ASSIGN_TO_NONNULL)
93
			if (this.nullCheckTypes[i] == ASSIGN_TO_NONNULL)
92
				this.parent.recordNullityMismatch(scope, this.nullReferences[i],
94
				this.parent.recordNullityMismatch(scope, (Expression)this.nullReferences[i],
93
						flowInfo.nullStatus(this.nullVariables[i]), this.expectedTypes[i]);
95
						flowInfo.nullStatus(this.nullVariables[i]), this.expectedTypes[i]);
94
			else
96
			else
95
				this.parent.recordUsingNullReference(scope, this.nullVariables[i],
97
				this.parent.recordUsingNullReference(scope, this.nullVariables[i],
Lines 98-104 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
98
	}
100
	}
99
	else { // no enclosing loop, be as precise as possible right now
101
	else { // no enclosing loop, be as precise as possible right now
100
		for (int i = 0; i < this.nullCount; i++) {
102
		for (int i = 0; i < this.nullCount; i++) {
101
			Expression expression = this.nullReferences[i];
103
			ASTNode location = this.nullReferences[i];
102
			// final local variable
104
			// final local variable
103
			VariableBinding var = this.nullVariables[i];
105
			VariableBinding var = this.nullVariables[i];
104
			switch (this.nullCheckTypes[i]) {
106
			switch (this.nullCheckTypes[i]) {
Lines 107-117 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
107
					if (flowInfo.isDefinitelyNonNull(var)) {
109
					if (flowInfo.isDefinitelyNonNull(var)) {
108
						if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
110
						if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
109
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
111
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
110
								scope.problemReporter().variableRedundantCheckOnNonNull(var, expression);
112
								scope.problemReporter().variableRedundantCheckOnNonNull(var, location);
111
							}
113
							}
112
						} else {
114
						} else {
113
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
115
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
114
								scope.problemReporter().variableNonNullComparedToNull(var, expression);
116
								scope.problemReporter().variableNonNullComparedToNull(var, location);
115
							}
117
							}
116
						}
118
						}
117
						continue;
119
						continue;
Lines 121-126 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
121
				case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
123
				case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
122
				case CAN_ONLY_NULL | IN_ASSIGNMENT:
124
				case CAN_ONLY_NULL | IN_ASSIGNMENT:
123
				case CAN_ONLY_NULL | IN_INSTANCEOF:
125
				case CAN_ONLY_NULL | IN_INSTANCEOF:
126
					Expression expression = (Expression) location;
124
					if (flowInfo.isDefinitelyNull(var)) {
127
					if (flowInfo.isDefinitelyNull(var)) {
125
						switch(this.nullCheckTypes[i] & CONTEXT_MASK) {
128
						switch(this.nullCheckTypes[i] & CONTEXT_MASK) {
126
							case FlowContext.IN_COMPARISON_NULL:
129
							case FlowContext.IN_COMPARISON_NULL:
Lines 169-186 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
169
					break;
172
					break;
170
				case MAY_NULL:
173
				case MAY_NULL:
171
					if (flowInfo.isDefinitelyNull(var)) {
174
					if (flowInfo.isDefinitelyNull(var)) {
172
						scope.problemReporter().variableNullReference(var, expression);
175
						scope.problemReporter().variableNullReference(var, location);
173
						continue;
176
						continue;
174
					}
177
					}
175
					if (flowInfo.isPotentiallyNull(var)) {
178
					if (flowInfo.isPotentiallyNull(var)) {
176
						scope.problemReporter().variablePotentialNullReference(var, expression);
179
						scope.problemReporter().variablePotentialNullReference(var, location);
177
					}
180
					}
178
					break;
181
					break;
179
				case ASSIGN_TO_NONNULL:
182
				case ASSIGN_TO_NONNULL:
180
					int nullStatus = flowInfo.nullStatus(var);
183
					int nullStatus = flowInfo.nullStatus(var);
181
					if (nullStatus != FlowInfo.NON_NULL) {
184
					if (nullStatus != FlowInfo.NON_NULL) {
182
						char[][] annotationName = scope.environment().getNonNullAnnotationName();
185
						char[][] annotationName = scope.environment().getNonNullAnnotationName();
183
						scope.problemReporter().nullityMismatch(expression, this.expectedTypes[i], nullStatus, annotationName);
186
						scope.problemReporter().nullityMismatch((Expression) location, this.expectedTypes[i], nullStatus, annotationName);
184
					}
187
					}
185
					break;
188
					break;
186
				default:
189
				default:
Lines 229-235 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
229
	}
232
	}
230
233
231
	public void recordUsingNullReference(Scope scope, VariableBinding var,
234
	public void recordUsingNullReference(Scope scope, VariableBinding var,
232
			Expression reference, int checkType, FlowInfo flowInfo) {
235
			ASTNode location, int checkType, FlowInfo flowInfo) {
233
		if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0 && !flowInfo.isDefinitelyUnknown(var))	{
236
		if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0 && !flowInfo.isDefinitelyUnknown(var))	{
234
			if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative
237
			if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative
235
				switch (checkType) {
238
				switch (checkType) {
Lines 239-244 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
239
					case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
242
					case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
240
					case CAN_ONLY_NULL | IN_ASSIGNMENT:
243
					case CAN_ONLY_NULL | IN_ASSIGNMENT:
241
					case CAN_ONLY_NULL | IN_INSTANCEOF:
244
					case CAN_ONLY_NULL | IN_INSTANCEOF:
245
						Expression reference = (Expression) location;
242
						if (flowInfo.cannotBeNull(var)) {
246
						if (flowInfo.cannotBeNull(var)) {
243
							if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
247
							if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
244
								if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
248
								if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
Lines 312-318 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
312
							return;
316
							return;
313
						}
317
						}
314
						if (flowInfo.canOnlyBeNull(var)) {
318
						if (flowInfo.canOnlyBeNull(var)) {
315
							scope.problemReporter().variableNullReference(var, reference);
319
							scope.problemReporter().variableNullReference(var, location);
316
							return;
320
							return;
317
						}
321
						}
318
						break;
322
						break;
Lines 327-340 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
327
						if (flowInfo.isDefinitelyNonNull(var)) {
331
						if (flowInfo.isDefinitelyNonNull(var)) {
328
							if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
332
							if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
329
								if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
333
								if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
330
									scope.problemReporter().variableRedundantCheckOnNonNull(var, reference);
334
									scope.problemReporter().variableRedundantCheckOnNonNull(var, location);
331
								}
335
								}
332
								if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(var)) {
336
								if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(var)) {
333
									flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
337
									flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
334
								}
338
								}
335
							} else {
339
							} else {
336
								if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
340
								if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
337
									scope.problemReporter().variableNonNullComparedToNull(var, reference);
341
									scope.problemReporter().variableNonNullComparedToNull(var, location);
338
								}
342
								}
339
								if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(var)) {
343
								if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(var)) {
340
									flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
344
									flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
Lines 347-352 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
347
					case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
351
					case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
348
					case CAN_ONLY_NULL | IN_ASSIGNMENT:
352
					case CAN_ONLY_NULL | IN_ASSIGNMENT:
349
					case CAN_ONLY_NULL | IN_INSTANCEOF:
353
					case CAN_ONLY_NULL | IN_INSTANCEOF:
354
						Expression reference = (Expression) location;
350
						if (flowInfo.isDefinitelyNull(var)) {
355
						if (flowInfo.isDefinitelyNull(var)) {
351
							switch(checkType & CONTEXT_MASK) {
356
							switch(checkType & CONTEXT_MASK) {
352
								case FlowContext.IN_COMPARISON_NULL:
357
								case FlowContext.IN_COMPARISON_NULL:
Lines 399-409 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
399
						break;
404
						break;
400
					case MAY_NULL :
405
					case MAY_NULL :
401
						if (flowInfo.isDefinitelyNull(var)) {
406
						if (flowInfo.isDefinitelyNull(var)) {
402
							scope.problemReporter().variableNullReference(var, reference);
407
							scope.problemReporter().variableNullReference(var, location);
403
							return;
408
							return;
404
						}
409
						}
405
						if (flowInfo.isPotentiallyNull(var)) {
410
						if (flowInfo.isPotentiallyNull(var)) {
406
							scope.problemReporter().variablePotentialNullReference(var, reference);
411
							scope.problemReporter().variablePotentialNullReference(var, location);
407
							return;
412
							return;
408
						}
413
						}
409
						if (flowInfo.isDefinitelyNonNull(var)) {
414
						if (flowInfo.isDefinitelyNonNull(var)) {
Lines 419-425 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
419
			if(((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) || checkType == MAY_NULL
424
			if(((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) || checkType == MAY_NULL
420
					|| (checkType & CONTEXT_MASK) == FlowContext.IN_ASSIGNMENT
425
					|| (checkType & CONTEXT_MASK) == FlowContext.IN_ASSIGNMENT
421
					|| (checkType & CONTEXT_MASK) == FlowContext.IN_INSTANCEOF) {
426
					|| (checkType & CONTEXT_MASK) == FlowContext.IN_INSTANCEOF) {
422
				recordNullReference(var, reference, checkType);
427
				recordNullReference(var, location, checkType);
423
			}
428
			}
424
			// prepare to re-check with try/catch flow info
429
			// prepare to re-check with try/catch flow info
425
		}
430
		}
Lines 436-442 public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { Link Here
436
	}
441
	}
437
442
438
protected void recordNullReference(VariableBinding var,
443
protected void recordNullReference(VariableBinding var,
439
	Expression expression, int status) {
444
	ASTNode expression, int status) {
440
	if (this.nullCount == 0) {
445
	if (this.nullCount == 0) {
441
		this.nullVariables = new VariableBinding[5];
446
		this.nullVariables = new VariableBinding[5];
442
		this.nullReferences = new Expression[5];
447
		this.nullReferences = new Expression[5];
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java (-10 / +22 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2011 IBM Corporation and others.
2
 * Copyright (c) 2000, 2012 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 19-24 import org.eclipse.jdt.core.compiler.CharOperation; Link Here
19
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
19
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
20
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
20
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
21
import org.eclipse.jdt.internal.compiler.ast.Expression;
21
import org.eclipse.jdt.internal.compiler.ast.Expression;
22
import org.eclipse.jdt.internal.compiler.ast.FakedTrackingVariable;
22
import org.eclipse.jdt.internal.compiler.ast.LabeledStatement;
23
import org.eclipse.jdt.internal.compiler.ast.LabeledStatement;
23
import org.eclipse.jdt.internal.compiler.ast.Reference;
24
import org.eclipse.jdt.internal.compiler.ast.Reference;
24
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
25
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
Lines 74-79 public static final int CAN_ONLY_NON_NULL = 0x0002; Link Here
74
public static final int MAY_NULL = 0x0003;
75
public static final int MAY_NULL = 0x0003;
75
//check binding a value to a @NonNull variable 
76
//check binding a value to a @NonNull variable 
76
public final static int ASSIGN_TO_NONNULL = 0x0080;
77
public final static int ASSIGN_TO_NONNULL = 0x0080;
78
//check against unclosed resource at early exit:
79
public static final int EXIT_RESOURCE = 0x0800;
77
// check against null, with potential values -- NPE guard
80
// check against null, with potential values -- NPE guard
78
public static final int CHECK_MASK = 0x00FF;
81
public static final int CHECK_MASK = 0x00FF;
79
public static final int IN_COMPARISON_NULL = 0x0100;
82
public static final int IN_COMPARISON_NULL = 0x0100;
Lines 556-561 public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) Link Here
556
	// default implementation: do nothing
559
	// default implementation: do nothing
557
}
560
}
558
561
562
public boolean recordExitAgainstResource(BlockScope scope, FlowInfo flowInfo, FakedTrackingVariable trackingVar, ASTNode reference) {
563
	return false; // not handled
564
}
565
559
protected void recordExpectedType(TypeBinding expectedType, int nullCount) {
566
protected void recordExpectedType(TypeBinding expectedType, int nullCount) {
560
	if (nullCount == 0) {
567
	if (nullCount == 0) {
561
		this.expectedTypes = new TypeBinding[5];
568
		this.expectedTypes = new TypeBinding[5];
Lines 580-586 protected boolean recordFinalAssignment(VariableBinding variable, Reference fina Link Here
580
 * Record a null reference for use by deferred checks. Only looping or
587
 * Record a null reference for use by deferred checks. Only looping or
581
 * finally contexts really record that information.
588
 * finally contexts really record that information.
582
 * @param local the local variable involved in the check
589
 * @param local the local variable involved in the check
583
 * @param expression the expression within which local lays
590
 * @param location the location triggering the analysis, for normal null dereference
591
 *      this is an expression resolving to 'local', for resource leaks it is an
592
 *      early exit statement.
584
 * @param status the status against which the check must be performed; one of
593
 * @param status the status against which the check must be performed; one of
585
 * 		{@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL
594
 * 		{@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL
586
 * 		CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL},
595
 * 		CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL},
Lines 589-595 protected boolean recordFinalAssignment(VariableBinding variable, Reference fina Link Here
589
 *      {@link #IN_COMPARISON_NON_NULL}, {@link #IN_ASSIGNMENT} or {@link #IN_INSTANCEOF})
598
 *      {@link #IN_COMPARISON_NON_NULL}, {@link #IN_ASSIGNMENT} or {@link #IN_INSTANCEOF})
590
 */
599
 */
591
protected void recordNullReference(VariableBinding local,
600
protected void recordNullReference(VariableBinding local,
592
	Expression expression, int status) {
601
	ASTNode location, int status) {
593
	// default implementation: do nothing
602
	// default implementation: do nothing
594
}
603
}
595
604
Lines 620-626 public void recordSettingFinal(VariableBinding variable, Reference finalReferenc Link Here
620
 * context).
629
 * context).
621
 * @param scope the scope into which the check is performed
630
 * @param scope the scope into which the check is performed
622
 * @param local the local variable involved in the check
631
 * @param local the local variable involved in the check
623
 * @param reference the expression within which local lies
632
 * @param location the location triggering the analysis, for normal null dereference
633
 *      this is an expression resolving to 'local', for resource leaks it is an
634
 *      early exit statement.
624
 * @param checkType the status against which the check must be performed; one
635
 * @param checkType the status against which the check must be performed; one
625
 * 		of {@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL
636
 * 		of {@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL
626
 * 		CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL}, potentially
637
 * 		CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL}, potentially
Lines 632-638 public void recordSettingFinal(VariableBinding variable, Reference finalReferenc Link Here
632
 * 		code that follows the current point)
643
 * 		code that follows the current point)
633
 */
644
 */
634
public void recordUsingNullReference(Scope scope, VariableBinding local,
645
public void recordUsingNullReference(Scope scope, VariableBinding local,
635
		Expression reference, int checkType, FlowInfo flowInfo) {
646
		ASTNode location, int checkType, FlowInfo flowInfo) {
636
	if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 ||
647
	if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 ||
637
			flowInfo.isDefinitelyUnknown(local)) {
648
			flowInfo.isDefinitelyUnknown(local)) {
638
		return;
649
		return;
Lines 643-656 public void recordUsingNullReference(Scope scope, VariableBinding local, Link Here
643
			if (flowInfo.isDefinitelyNonNull(local)) {
654
			if (flowInfo.isDefinitelyNonNull(local)) {
644
				if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
655
				if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
645
					if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
656
					if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
646
						scope.problemReporter().variableRedundantCheckOnNonNull(local, reference);
657
						scope.problemReporter().variableRedundantCheckOnNonNull(local, location);
647
					}
658
					}
648
					if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
659
					if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
649
						flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
660
						flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
650
					}
661
					}
651
				} else {
662
				} else {
652
					if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
663
					if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
653
						scope.problemReporter().variableNonNullComparedToNull(local, reference);
664
						scope.problemReporter().variableNonNullComparedToNull(local, location);
654
					}
665
					}
655
					if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
666
					if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
656
						flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
667
						flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
Lines 666-671 public void recordUsingNullReference(Scope scope, VariableBinding local, Link Here
666
		case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
677
		case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
667
		case CAN_ONLY_NULL | IN_ASSIGNMENT:
678
		case CAN_ONLY_NULL | IN_ASSIGNMENT:
668
		case CAN_ONLY_NULL | IN_INSTANCEOF:
679
		case CAN_ONLY_NULL | IN_INSTANCEOF:
680
			Expression reference = (Expression)location;
669
			if (flowInfo.isDefinitelyNull(local)) {
681
			if (flowInfo.isDefinitelyNull(local)) {
670
				switch(checkType & CONTEXT_MASK) {
682
				switch(checkType & CONTEXT_MASK) {
671
					case FlowContext.IN_COMPARISON_NULL:
683
					case FlowContext.IN_COMPARISON_NULL:
Lines 720-730 public void recordUsingNullReference(Scope scope, VariableBinding local, Link Here
720
			break;
732
			break;
721
		case MAY_NULL :
733
		case MAY_NULL :
722
			if (flowInfo.isDefinitelyNull(local)) {
734
			if (flowInfo.isDefinitelyNull(local)) {
723
				scope.problemReporter().variableNullReference(local, reference);
735
				scope.problemReporter().variableNullReference(local, location);
724
				return;
736
				return;
725
			}
737
			}
726
			if (flowInfo.isPotentiallyNull(local)) {
738
			if (flowInfo.isPotentiallyNull(local)) {
727
				scope.problemReporter().variablePotentialNullReference(local, reference);
739
				scope.problemReporter().variablePotentialNullReference(local, location);
728
				return;
740
				return;
729
			}
741
			}
730
			break;
742
			break;
Lines 732-738 public void recordUsingNullReference(Scope scope, VariableBinding local, Link Here
732
			// never happens
744
			// never happens
733
	}
745
	}
734
	if (this.parent != null) {
746
	if (this.parent != null) {
735
		this.parent.recordUsingNullReference(scope, local, reference, checkType,
747
		this.parent.recordUsingNullReference(scope, local, location, checkType,
736
				flowInfo);
748
				flowInfo);
737
	}
749
	}
738
}
750
}
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java (-24 / +84 lines)
Lines 18-23 import java.util.ArrayList; Link Here
18
18
19
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
19
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
20
import org.eclipse.jdt.internal.compiler.ast.Expression;
20
import org.eclipse.jdt.internal.compiler.ast.Expression;
21
import org.eclipse.jdt.internal.compiler.ast.FakedTrackingVariable;
21
import org.eclipse.jdt.internal.compiler.ast.Reference;
22
import org.eclipse.jdt.internal.compiler.ast.Reference;
22
import org.eclipse.jdt.internal.compiler.codegen.BranchLabel;
23
import org.eclipse.jdt.internal.compiler.codegen.BranchLabel;
23
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
24
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
Lines 48-55 public class LoopingFlowContext extends SwitchFlowContext { Link Here
48
	VariableBinding finalVariables[];
49
	VariableBinding finalVariables[];
49
	int assignCount = 0;
50
	int assignCount = 0;
50
51
52
	// the following three arrays are in sync regarding their indices:
51
	VariableBinding[] nullVariables;
53
	VariableBinding[] nullVariables;
52
	Expression[] nullReferences;
54
	ASTNode[] nullReferences;	// Expressions for null checking, Statements for resource analysis
55
								// cast to Expression is safe if corresponding nullCheckType != EXIT_RESOURCE
53
	int[] nullCheckTypes;
56
	int[] nullCheckTypes;
54
	int nullCount;
57
	int nullCount;
55
	// see also the related field FlowContext#expectedTypes
58
	// see also the related field FlowContext#expectedTypes
Lines 146-152 public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn Link Here
146
		// check only immutable null checks on innermost looping context
149
		// check only immutable null checks on innermost looping context
147
		for (int i = 0; i < this.nullCount; i++) {
150
		for (int i = 0; i < this.nullCount; i++) {
148
			VariableBinding local = this.nullVariables[i];
151
			VariableBinding local = this.nullVariables[i];
149
			Expression expression = this.nullReferences[i];
152
			ASTNode location = this.nullReferences[i];
150
			// final local variable
153
			// final local variable
151
			switch (this.nullCheckTypes[i]) {
154
			switch (this.nullCheckTypes[i]) {
152
				case CAN_ONLY_NON_NULL | IN_COMPARISON_NULL:
155
				case CAN_ONLY_NON_NULL | IN_COMPARISON_NULL:
Lines 155-165 public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn Link Here
155
						this.nullReferences[i] = null;
158
						this.nullReferences[i] = null;
156
						if (this.nullCheckTypes[i] == (CAN_ONLY_NON_NULL | IN_COMPARISON_NON_NULL)) {
159
						if (this.nullCheckTypes[i] == (CAN_ONLY_NON_NULL | IN_COMPARISON_NON_NULL)) {
157
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
160
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
158
								scope.problemReporter().variableRedundantCheckOnNonNull(local, expression);
161
								scope.problemReporter().variableRedundantCheckOnNonNull(local, location);
159
							}
162
							}
160
						} else {
163
						} else {
161
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
164
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
162
								scope.problemReporter().variableNonNullComparedToNull(local, expression);
165
								scope.problemReporter().variableNonNullComparedToNull(local, location);
163
							}
166
							}
164
						}
167
						}
165
						continue;
168
						continue;
Lines 171-181 public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn Link Here
171
						this.nullReferences[i] = null;
174
						this.nullReferences[i] = null;
172
						if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
175
						if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
173
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
176
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
174
								scope.problemReporter().variableRedundantCheckOnNonNull(local, expression);
177
								scope.problemReporter().variableRedundantCheckOnNonNull(local, location);
175
							}
178
							}
176
						} else {
179
						} else {
177
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
180
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
178
								scope.problemReporter().variableNonNullComparedToNull(local, expression);
181
								scope.problemReporter().variableNonNullComparedToNull(local, location);
179
							}
182
							}
180
						}
183
						}
181
						continue;
184
						continue;
Lines 184-194 public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn Link Here
184
						this.nullReferences[i] = null;
187
						this.nullReferences[i] = null;
185
						if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) {
188
						if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) {
186
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
189
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
187
								scope.problemReporter().variableRedundantCheckOnNull(local, expression);
190
								scope.problemReporter().variableRedundantCheckOnNull(local, location);
188
							}
191
							}
189
						} else {
192
						} else {
190
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
193
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
191
								scope.problemReporter().variableNullComparedToNonNull(local, expression);
194
								scope.problemReporter().variableNullComparedToNonNull(local, location);
192
							}
195
							}
193
						}
196
						}
194
						continue;
197
						continue;
Lines 198-203 public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn Link Here
198
				case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
201
				case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
199
				case CAN_ONLY_NULL | IN_ASSIGNMENT:
202
				case CAN_ONLY_NULL | IN_ASSIGNMENT:
200
				case CAN_ONLY_NULL | IN_INSTANCEOF:
203
				case CAN_ONLY_NULL | IN_INSTANCEOF:
204
					Expression expression = (Expression)location;
201
					if (flowInfo.isDefinitelyNull(local)) {
205
					if (flowInfo.isDefinitelyNull(local)) {
202
						this.nullReferences[i] = null;
206
						this.nullReferences[i] = null;
203
						switch(this.nullCheckTypes[i] & CONTEXT_MASK) {
207
						switch(this.nullCheckTypes[i] & CONTEXT_MASK) {
Lines 248-271 public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn Link Here
248
				case MAY_NULL:
252
				case MAY_NULL:
249
					if (flowInfo.isDefinitelyNull(local)) {
253
					if (flowInfo.isDefinitelyNull(local)) {
250
						this.nullReferences[i] = null;
254
						this.nullReferences[i] = null;
251
						scope.problemReporter().variableNullReference(local, expression);
255
						scope.problemReporter().variableNullReference(local, location);
252
						continue;
256
						continue;
253
					}
257
					}
254
					break;
258
					break;
255
				case ASSIGN_TO_NONNULL:
259
				case ASSIGN_TO_NONNULL:
256
					this.parent.recordNullityMismatch(scope, expression, flowInfo.nullStatus(local), this.expectedTypes[i]);
260
					this.parent.recordNullityMismatch(scope, (Expression)location, flowInfo.nullStatus(local), this.expectedTypes[i]);
261
					break;
262
				case EXIT_RESOURCE:
263
					if (local instanceof LocalVariableBinding) {
264
						FakedTrackingVariable trackingVar = ((LocalVariableBinding) local).closeTracker;
265
						if (trackingVar != null) {
266
							if (trackingVar.hasDefinitelyNoResource(flowInfo)) {
267
								continue; // no resource - no warning.
268
							}
269
							if (trackingVar.isClosedInFinallyOfEnclosing(scope)) {
270
								continue;
271
							}
272
							if (this.parent.recordExitAgainstResource(scope, flowInfo, trackingVar, location)) {
273
								this.nullReferences[i] = null;
274
								continue;
275
							}
276
						}
277
					}
257
					break;
278
					break;
258
				default:
279
				default:
259
					// never happens
280
					// never happens
260
			}
281
			}
261
			this.parent.recordUsingNullReference(scope, local, expression,
282
			this.parent.recordUsingNullReference(scope, local, location,
262
					this.nullCheckTypes[i], flowInfo);
283
					this.nullCheckTypes[i], flowInfo);
263
		}
284
		}
264
	}
285
	}
265
	else {
286
	else {
266
		// check inconsistent null checks on outermost looping context
287
		// check inconsistent null checks on outermost looping context
267
		for (int i = 0; i < this.nullCount; i++) {
288
		for (int i = 0; i < this.nullCount; i++) {
268
			Expression expression = this.nullReferences[i];
289
			ASTNode location = this.nullReferences[i];
269
			// final local variable
290
			// final local variable
270
			VariableBinding local = this.nullVariables[i];
291
			VariableBinding local = this.nullVariables[i];
271
			switch (this.nullCheckTypes[i]) {
292
			switch (this.nullCheckTypes[i]) {
Lines 275-285 public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn Link Here
275
						this.nullReferences[i] = null;
296
						this.nullReferences[i] = null;
276
						if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
297
						if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
277
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
298
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
278
								scope.problemReporter().variableRedundantCheckOnNonNull(local, expression);
299
								scope.problemReporter().variableRedundantCheckOnNonNull(local, location);
279
							}
300
							}
280
						} else {
301
						} else {
281
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
302
							if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
282
								scope.problemReporter().variableNonNullComparedToNull(local, expression);
303
								scope.problemReporter().variableNonNullComparedToNull(local, location);
283
							}
304
							}
284
						}
305
						}
285
						continue;
306
						continue;
Lines 289-294 public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn Link Here
289
				case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
310
				case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
290
				case CAN_ONLY_NULL | IN_ASSIGNMENT:
311
				case CAN_ONLY_NULL | IN_ASSIGNMENT:
291
				case CAN_ONLY_NULL | IN_INSTANCEOF:
312
				case CAN_ONLY_NULL | IN_INSTANCEOF:
313
					Expression expression = (Expression) location;
292
					if (flowInfo.isDefinitelyNull(local)) {
314
					if (flowInfo.isDefinitelyNull(local)) {
293
						this.nullReferences[i] = null;
315
						this.nullReferences[i] = null;
294
						switch(this.nullCheckTypes[i] & CONTEXT_MASK) {
316
						switch(this.nullCheckTypes[i] & CONTEXT_MASK) {
Lines 339-350 public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn Link Here
339
				case MAY_NULL:
361
				case MAY_NULL:
340
					if (flowInfo.isDefinitelyNull(local)) {
362
					if (flowInfo.isDefinitelyNull(local)) {
341
						this.nullReferences[i] = null;
363
						this.nullReferences[i] = null;
342
						scope.problemReporter().variableNullReference(local, expression);
364
						scope.problemReporter().variableNullReference(local, location);
343
						continue;
365
						continue;
344
					}
366
					}
345
					if (flowInfo.isPotentiallyNull(local)) {
367
					if (flowInfo.isPotentiallyNull(local)) {
346
						this.nullReferences[i] = null;
368
						this.nullReferences[i] = null;
347
						scope.problemReporter().variablePotentialNullReference(local, expression);
369
						scope.problemReporter().variablePotentialNullReference(local, location);
348
						continue;
370
						continue;
349
					}
371
					}
350
					break;
372
					break;
Lines 352-358 public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn Link Here
352
					int nullStatus = flowInfo.nullStatus(local);
374
					int nullStatus = flowInfo.nullStatus(local);
353
					if (nullStatus != FlowInfo.NON_NULL) {
375
					if (nullStatus != FlowInfo.NON_NULL) {
354
						char[][] annotationName = scope.environment().getNonNullAnnotationName();
376
						char[][] annotationName = scope.environment().getNonNullAnnotationName();
355
						scope.problemReporter().nullityMismatch(expression, this.expectedTypes[i], nullStatus, annotationName);
377
						scope.problemReporter().nullityMismatch((Expression) location, this.expectedTypes[i], nullStatus, annotationName);
378
					}
379
					break;
380
				case EXIT_RESOURCE:
381
					nullStatus = flowInfo.nullStatus(local);
382
					if (nullStatus != FlowInfo.NON_NULL && local instanceof LocalVariableBinding) {
383
						FakedTrackingVariable closeTracker = ((LocalVariableBinding)local).closeTracker;
384
						if (closeTracker != null) {
385
							if (closeTracker.hasDefinitelyNoResource(flowInfo)) {
386
								continue; // no resource - no warning.
387
							}
388
							if (closeTracker.isClosedInFinallyOfEnclosing(scope)) {
389
								continue;
390
							}
391
							closeTracker.recordErrorLocation(this.nullReferences[i], nullStatus);
392
							closeTracker.reportRecordedErrors(scope, nullStatus);
393
							this.nullReferences[i] = null;
394
							continue;
395
						}
356
					}
396
					}
357
					break;
397
					break;
358
				default:
398
				default:
Lines 477-493 public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) Link Here
477
	}
517
	}
478
518
479
protected void recordNullReference(VariableBinding local,
519
protected void recordNullReference(VariableBinding local,
480
	Expression expression, int status) {
520
	ASTNode expression, int status) {
481
	if (this.nullCount == 0) {
521
	if (this.nullCount == 0) {
482
		this.nullVariables = new VariableBinding[5];
522
		this.nullVariables = new VariableBinding[5];
483
		this.nullReferences = new Expression[5];
523
		this.nullReferences = new ASTNode[5];
484
		this.nullCheckTypes = new int[5];
524
		this.nullCheckTypes = new int[5];
485
	}
525
	}
486
	else if (this.nullCount == this.nullVariables.length) {
526
	else if (this.nullCount == this.nullVariables.length) {
487
		System.arraycopy(this.nullVariables, 0,
527
		System.arraycopy(this.nullVariables, 0,
488
			this.nullVariables = new VariableBinding[this.nullCount * 2], 0, this.nullCount);
528
			this.nullVariables = new VariableBinding[this.nullCount * 2], 0, this.nullCount);
489
		System.arraycopy(this.nullReferences, 0,
529
		System.arraycopy(this.nullReferences, 0,
490
			this.nullReferences = new Expression[this.nullCount * 2], 0, this.nullCount);
530
			this.nullReferences = new ASTNode[this.nullCount * 2], 0, this.nullCount);
491
		System.arraycopy(this.nullCheckTypes, 0,
531
		System.arraycopy(this.nullCheckTypes, 0,
492
			this.nullCheckTypes = new int[this.nullCount * 2], 0, this.nullCount);
532
			this.nullCheckTypes = new int[this.nullCount * 2], 0, this.nullCount);
493
	}
533
	}
Lines 496-503 protected void recordNullReference(VariableBinding local, Link Here
496
	this.nullCheckTypes[this.nullCount++] = status;
536
	this.nullCheckTypes[this.nullCount++] = status;
497
}
537
}
498
538
539
/** Record the fact that we see an early exit (in 'reference') while 'trackingVar' is in scope and may be unclosed. */
540
public boolean recordExitAgainstResource(BlockScope scope, FlowInfo flowInfo, FakedTrackingVariable trackingVar, ASTNode reference) {
541
	LocalVariableBinding local = trackingVar.binding;
542
	if (flowInfo.isDefinitelyNonNull(local)) {
543
		return false;
544
	}
545
	if (flowInfo.isDefinitelyNull(local)) {
546
		scope.problemReporter().unclosedCloseable(trackingVar, reference);
547
		return true; // handled
548
	}
549
	if (flowInfo.isPotentiallyNull(local)) {
550
		scope.problemReporter().potentiallyUnclosedCloseable(trackingVar, reference);
551
		return true; // handled
552
	}
553
	recordNullReference(trackingVar.binding, reference, EXIT_RESOURCE);
554
	return true; // handled
555
}
556
499
public void recordUsingNullReference(Scope scope, VariableBinding local,
557
public void recordUsingNullReference(Scope scope, VariableBinding local,
500
		Expression reference, int checkType, FlowInfo flowInfo) {
558
		ASTNode location, int checkType, FlowInfo flowInfo) {
501
	if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 ||
559
	if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 ||
502
			flowInfo.isDefinitelyUnknown(local)) {
560
			flowInfo.isDefinitelyUnknown(local)) {
503
		return;
561
		return;
Lines 505-510 public void recordUsingNullReference(Scope scope, VariableBinding local, Link Here
505
	switch (checkType) {
563
	switch (checkType) {
506
		case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
564
		case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
507
		case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
565
		case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
566
			Expression reference = (Expression)location;
508
			if (flowInfo.isDefinitelyNonNull(local)) {
567
			if (flowInfo.isDefinitelyNonNull(local)) {
509
				if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
568
				if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
510
					if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
569
					if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
Lines 564-569 public void recordUsingNullReference(Scope scope, VariableBinding local, Link Here
564
		case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
623
		case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
565
		case CAN_ONLY_NULL | IN_ASSIGNMENT:
624
		case CAN_ONLY_NULL | IN_ASSIGNMENT:
566
		case CAN_ONLY_NULL | IN_INSTANCEOF:
625
		case CAN_ONLY_NULL | IN_INSTANCEOF:
626
			reference = (Expression)location;
567
			if (flowInfo.isPotentiallyNonNull(local)
627
			if (flowInfo.isPotentiallyNonNull(local)
568
					|| flowInfo.isPotentiallyUnknown(local)
628
					|| flowInfo.isPotentiallyUnknown(local)
569
					|| flowInfo.isProtectedNonNull(local)) {
629
					|| flowInfo.isProtectedNonNull(local)) {
Lines 633-646 public void recordUsingNullReference(Scope scope, VariableBinding local, Link Here
633
				return;
693
				return;
634
			}
694
			}
635
			if (flowInfo.isDefinitelyNull(local)) {
695
			if (flowInfo.isDefinitelyNull(local)) {
636
				scope.problemReporter().variableNullReference(local, reference);
696
				scope.problemReporter().variableNullReference(local, location);
637
				return;
697
				return;
638
			}
698
			}
639
			if (flowInfo.isPotentiallyNull(local)) {
699
			if (flowInfo.isPotentiallyNull(local)) {
640
				scope.problemReporter().variablePotentialNullReference(local, reference);
700
				scope.problemReporter().variablePotentialNullReference(local, location);
641
				return;
701
				return;
642
			}
702
			}
643
			recordNullReference(local, reference, checkType);
703
			recordNullReference(local, location, checkType);
644
			return;
704
			return;
645
		default:
705
		default:
646
			// never happens
706
			// never happens
(-)a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java (-5 / +11 lines)
Lines 23-28 import org.eclipse.jdt.core.compiler.CharOperation; Link Here
23
import org.eclipse.jdt.internal.compiler.ast.*;
23
import org.eclipse.jdt.internal.compiler.ast.*;
24
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
24
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
25
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
25
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
26
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
26
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
27
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
27
import org.eclipse.jdt.internal.compiler.impl.Constant;
28
import org.eclipse.jdt.internal.compiler.impl.Constant;
28
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
29
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
Lines 1001-1011 public void pruneWrapperTrackingVar(FakedTrackingVariable trackingVariable) { Link Here
1001
 * At the end of a block check the closing-status of all tracked closeables that are declared in this block.
1002
 * At the end of a block check the closing-status of all tracked closeables that are declared in this block.
1002
 * Also invoked when entering unreachable code.
1003
 * Also invoked when entering unreachable code.
1003
 */
1004
 */
1004
public void checkUnclosedCloseables(FlowInfo flowInfo, ASTNode location, BlockScope locationScope) {
1005
public void checkUnclosedCloseables(FlowInfo flowInfo, FlowContext flowContext, ASTNode location, BlockScope locationScope) {
1005
	if (this.trackingVariables == null) {
1006
	if (this.trackingVariables == null) {
1006
		// at a method return we also consider enclosing scopes
1007
		// at a method return we also consider enclosing scopes
1007
		if (location != null && this.parent instanceof BlockScope)
1008
		if (location != null && this.parent instanceof BlockScope)
1008
			((BlockScope) this.parent).checkUnclosedCloseables(flowInfo, location, locationScope);
1009
			((BlockScope) this.parent).checkUnclosedCloseables(flowInfo, flowContext, location, locationScope);
1009
		return;
1010
		return;
1010
	}
1011
	}
1011
	if (location != null && flowInfo.reachMode() != 0) return;
1012
	if (location != null && flowInfo.reachMode() != 0) return;
Lines 1022-1030 public void checkUnclosedCloseables(FlowInfo flowInfo, ASTNode location, BlockSc Link Here
1022
			continue;
1023
			continue;
1023
		}
1024
		}
1024
1025
1025
		if (location != null && trackingVar.originalBinding != null && flowInfo.isDefinitelyNull(trackingVar.originalBinding))
1026
		if (location != null && trackingVar.hasDefinitelyNoResource(flowInfo)) {
1026
			continue; // reporting against a specific location, resource is null at this flow, don't complain
1027
			continue; // reporting against a specific location, there is no resource at this flow, don't complain
1027
		
1028
		}
1029
1030
		if (location != null && flowContext != null && flowContext.recordExitAgainstResource(this, flowInfo, trackingVar, location)) {
1031
			continue; // handled by the flow context
1032
		}
1033
1028
		// compute the most specific null status for this resource,
1034
		// compute the most specific null status for this resource,
1029
		int status = trackingVar.findMostSpecificStatus(flowInfo, this, locationScope);
1035
		int status = trackingVar.findMostSpecificStatus(flowInfo, this, locationScope);
1030
		
1036
		

Return to bug 368546