/src/EditorFeatures/CSharpTest/Diagnostics/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessTests.cs

https://gitlab.com/sharadag/Roslyn · C# · 636 lines · 582 code · 26 blank · 28 comment · 0 complexity · c89038dabb59c421dec4decea48577cc MD5 · raw file

  1. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using Microsoft.CodeAnalysis.CodeFixes;
  8. using Microsoft.CodeAnalysis.CSharp.InvokeDelegateWithConditionalAccess;
  9. using Microsoft.CodeAnalysis.Diagnostics;
  10. using Roslyn.Test.Utilities;
  11. using Xunit;
  12. namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.InvokeDelegateWithConditionalAccess
  13. {
  14. public class InvokeDelegateWithConditionalAccessTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
  15. {
  16. internal override Tuple<DiagnosticAnalyzer, CodeFixProvider> CreateDiagnosticProviderAndFixer(Workspace workspace)
  17. {
  18. return new Tuple<DiagnosticAnalyzer, CodeFixProvider>(
  19. new InvokeDelegateWithConditionalAccessAnalyzer(),
  20. new InvokeDelegateWithConditionalAccessCodeFixProvider());
  21. }
  22. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  23. public async Task Test1()
  24. {
  25. await TestAsync(
  26. @"class C
  27. {
  28. System.Action a;
  29. void Foo()
  30. {
  31. [||]var v = a;
  32. if (v != null)
  33. {
  34. v();
  35. }
  36. }
  37. }",
  38. @"
  39. class C
  40. {
  41. System.Action a;
  42. void Foo()
  43. {
  44. a?.Invoke();
  45. }
  46. }");
  47. }
  48. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  49. public async Task TestInvertedIf()
  50. {
  51. await TestAsync(
  52. @"class C
  53. {
  54. System.Action a;
  55. void Foo()
  56. {
  57. [||]var v = a;
  58. if (null != v)
  59. {
  60. v();
  61. }
  62. }
  63. }",
  64. @"
  65. class C
  66. {
  67. System.Action a;
  68. void Foo()
  69. {
  70. a?.Invoke();
  71. }
  72. }");
  73. }
  74. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  75. public async Task TestIfWithNoBraces()
  76. {
  77. await TestAsync(
  78. @"class C
  79. {
  80. System.Action a;
  81. void Foo()
  82. {
  83. [||]var v = a;
  84. if (null != v)
  85. v();
  86. }
  87. }",
  88. @"
  89. class C
  90. {
  91. System.Action a;
  92. void Foo()
  93. {
  94. a?.Invoke();
  95. }
  96. }");
  97. }
  98. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  99. public async Task TestWithComplexExpression()
  100. {
  101. await TestAsync(
  102. @"class C
  103. {
  104. System.Action a;
  105. void Foo()
  106. {
  107. bool b = true;
  108. [||]var v = b ? a : null;
  109. if (v != null)
  110. {
  111. v();
  112. }
  113. }
  114. }",
  115. @"
  116. class C
  117. {
  118. System.Action a;
  119. void Foo()
  120. {
  121. bool b = true;
  122. (b ? a : null)?.Invoke();
  123. }
  124. }");
  125. }
  126. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  127. public async Task TestMissingWithElseClause()
  128. {
  129. await TestMissingAsync(
  130. @"class C
  131. {
  132. System.Action a;
  133. void Foo()
  134. {
  135. [||]var v = a;
  136. if (v != null)
  137. {
  138. v();
  139. }
  140. else {}
  141. }
  142. }");
  143. }
  144. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  145. public async Task TestMissingOnDeclarationWithMultipleVariables()
  146. {
  147. await TestMissingAsync(
  148. @"class C
  149. {
  150. System.Action a;
  151. void Foo()
  152. {
  153. [||]var v = a, x = a;
  154. if (v != null)
  155. {
  156. v();
  157. }
  158. }
  159. }");
  160. }
  161. /// <remarks>
  162. /// With multiple variables in the same declaration, the fix _is not_ offered on the declaration
  163. /// itself, but _is_ offered on the invocation pattern.
  164. /// </remarks>
  165. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  166. public async Task TestLocationWhereOfferedWithMultipleVariables()
  167. {
  168. await TestAsync(
  169. @"class C
  170. {
  171. System.Action a;
  172. void Foo()
  173. {
  174. var v = a, x = a;
  175. [||]if (v != null)
  176. {
  177. v();
  178. }
  179. }
  180. }",
  181. @"class C
  182. {
  183. System.Action a;
  184. void Foo()
  185. {
  186. var v = a, x = a;
  187. v?.Invoke();
  188. }
  189. }");
  190. }
  191. /// <remarks>
  192. /// If we have a variable declaration and if it is read/written outside the delegate
  193. /// invocation pattern, the fix is not offered on the declaration.
  194. /// </remarks>
  195. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  196. public async Task TestMissingOnDeclarationIfUsedOutside()
  197. {
  198. await TestMissingAsync(
  199. @"class C
  200. {
  201. System.Action a;
  202. void Foo()
  203. {
  204. [||]var v = a;
  205. if (v != null)
  206. {
  207. v();
  208. }
  209. v = null;
  210. }
  211. }");
  212. }
  213. /// <remarks>
  214. /// If we have a variable declaration and if it is read/written outside the delegate
  215. /// invocation pattern, the fix is not offered on the declaration but is offered on
  216. /// the invocation pattern itself.
  217. /// </remarks>
  218. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  219. public async Task TestLocationWhereOfferedIfUsedOutside()
  220. {
  221. await TestAsync(
  222. @"class C
  223. {
  224. System.Action a;
  225. void Foo()
  226. {
  227. var v = a;
  228. [||]if (v != null)
  229. {
  230. v();
  231. }
  232. v = null;
  233. }
  234. }",
  235. @"class C
  236. {
  237. System.Action a;
  238. void Foo()
  239. {
  240. var v = a;
  241. v?.Invoke();
  242. v = null;
  243. }
  244. }");
  245. }
  246. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  247. public async Task TestSimpleForm1()
  248. {
  249. await TestAsync(
  250. @"
  251. using System;
  252. class C
  253. {
  254. public event EventHandler E;
  255. void M()
  256. {
  257. [||]if (this.E != null)
  258. {
  259. this.E(this, EventArgs.Empty);
  260. }
  261. }
  262. }",
  263. @"
  264. using System;
  265. class C
  266. {
  267. public event EventHandler E;
  268. void M()
  269. {
  270. this.E?.Invoke(this, EventArgs.Empty);
  271. }
  272. }");
  273. }
  274. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  275. public async Task TestInElseClause1()
  276. {
  277. await TestAsync(
  278. @"
  279. using System;
  280. class C
  281. {
  282. public event EventHandler E;
  283. void M()
  284. {
  285. if (true != true)
  286. {
  287. }
  288. else [||]if (this.E != null)
  289. {
  290. this.E(this, EventArgs.Empty);
  291. }
  292. }
  293. }",
  294. @"
  295. using System;
  296. class C
  297. {
  298. public event EventHandler E;
  299. void M()
  300. {
  301. if (true != true)
  302. {
  303. }
  304. else
  305. {
  306. this.E?.Invoke(this, EventArgs.Empty);
  307. }
  308. }
  309. }");
  310. }
  311. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  312. public async Task TestInElseClause2()
  313. {
  314. await TestAsync(
  315. @"
  316. using System;
  317. class C
  318. {
  319. public event EventHandler E;
  320. void M()
  321. {
  322. if (true != true)
  323. {
  324. }
  325. else [||]if (this.E != null)
  326. this.E(this, EventArgs.Empty);
  327. }
  328. }",
  329. @"
  330. using System;
  331. class C
  332. {
  333. public event EventHandler E;
  334. void M()
  335. {
  336. if (true != true)
  337. {
  338. }
  339. else this.E?.Invoke(this, EventArgs.Empty);
  340. }
  341. }");
  342. }
  343. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  344. public async Task TestTrivia1()
  345. {
  346. await TestAsync(
  347. @"class C
  348. {
  349. System.Action a;
  350. void Foo()
  351. {
  352. // Comment
  353. [||]var v = a;
  354. if (v != null)
  355. {
  356. v();
  357. }
  358. }
  359. }",
  360. @"class C
  361. {
  362. System.Action a;
  363. void Foo()
  364. {
  365. // Comment
  366. a?.Invoke();
  367. }
  368. }", compareTokens: false);
  369. }
  370. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  371. public async Task TestTrivia2()
  372. {
  373. await TestAsync(
  374. @"class C
  375. {
  376. System.Action a;
  377. void Foo()
  378. {
  379. // Comment
  380. [||]if (a != null)
  381. {
  382. a();
  383. }
  384. }
  385. }",
  386. @"class C
  387. {
  388. System.Action a;
  389. void Foo()
  390. {
  391. // Comment
  392. a?.Invoke();
  393. }
  394. }", compareTokens: false);
  395. }
  396. /// <remarks>
  397. /// tests locations where the fix is offered.
  398. /// </remarks>
  399. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  400. public async Task TestFixOfferedOnIf()
  401. {
  402. await TestAsync(
  403. @"class C
  404. {
  405. System.Action a;
  406. void Foo()
  407. {
  408. var v = a;
  409. [||]if (v != null)
  410. {
  411. v();
  412. }
  413. }
  414. }",
  415. @"
  416. class C
  417. {
  418. System.Action a;
  419. void Foo()
  420. {
  421. a?.Invoke();
  422. }
  423. }");
  424. }
  425. /// <remarks>
  426. /// tests locations where the fix is offered.
  427. /// </remarks>
  428. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  429. public async Task TestFixOfferedInsideIf()
  430. {
  431. await TestAsync(
  432. @"class C
  433. {
  434. System.Action a;
  435. void Foo()
  436. {
  437. var v = a;
  438. if (v != null)
  439. {
  440. [||]v();
  441. }
  442. }
  443. }",
  444. @"
  445. class C
  446. {
  447. System.Action a;
  448. void Foo()
  449. {
  450. a?.Invoke();
  451. }
  452. }");
  453. }
  454. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  455. public async Task TestMissingOnConditionalInvocation()
  456. {
  457. await TestMissingAsync(
  458. @"class C
  459. {
  460. System.Action a;
  461. void Foo()
  462. {
  463. [||]var v = a;
  464. v?.Invoke();
  465. }
  466. }");
  467. }
  468. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  469. public async Task TestMissingOnConditionalInvocation2()
  470. {
  471. await TestMissingAsync(
  472. @"class C
  473. {
  474. System.Action a;
  475. void Foo()
  476. {
  477. var v = a;
  478. [||]v?.Invoke();
  479. }
  480. }");
  481. }
  482. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  483. public async Task TestMissingOnConditionalInvocation3()
  484. {
  485. await TestMissingAsync(
  486. @"class C
  487. {
  488. System.Action a;
  489. void Foo()
  490. {
  491. [||]a?.Invoke();
  492. }
  493. }");
  494. }
  495. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  496. public async Task TestMissingOnNonNullCheckExpressions()
  497. {
  498. await TestMissingAsync(
  499. @"class C
  500. {
  501. System.Action a;
  502. void Foo()
  503. {
  504. var v = a;
  505. if (v == a)
  506. {
  507. [||]v();
  508. }
  509. }
  510. }");
  511. }
  512. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  513. public async Task TestMissingOnNonNullCheckExpressions2()
  514. {
  515. await TestMissingAsync(
  516. @"class C
  517. {
  518. System.Action a;
  519. void Foo()
  520. {
  521. var v = a;
  522. if (v == null)
  523. {
  524. [||]v();
  525. }
  526. }
  527. }");
  528. }
  529. /// <remarks>
  530. /// if local declaration is not immediately preceding the invocation pattern,
  531. /// the fix is not offered on the declaration.
  532. /// </remarks>
  533. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  534. public async Task TestLocalNotImmediatelyPrecedingNullCheckAndInvokePattern()
  535. {
  536. await TestMissingAsync(
  537. @"class C
  538. {
  539. System.Action a;
  540. void Foo()
  541. {
  542. [||]var v = a;
  543. int x;
  544. if (v != null)
  545. {
  546. v();
  547. }
  548. }
  549. }");
  550. }
  551. /// <remarks>
  552. /// if local declaration is not immediately preceding the invocation pattern,
  553. /// the fix is not offered on the declaration but is offered on the invocation pattern itself.
  554. /// </remarks>
  555. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  556. public async Task TestLocalDNotImmediatelyPrecedingNullCheckAndInvokePattern2()
  557. {
  558. await TestAsync(
  559. @"class C
  560. {
  561. System.Action a;
  562. void Foo()
  563. {
  564. var v = a;
  565. int x;
  566. [||]if (v != null)
  567. {
  568. v();
  569. }
  570. }
  571. }",
  572. @"class C
  573. {
  574. System.Action a;
  575. void Foo()
  576. {
  577. var v = a;
  578. int x;
  579. v?.Invoke();
  580. }
  581. }");
  582. }
  583. [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)]
  584. public async Task TestMissingOnFunc()
  585. {
  586. await TestMissingAsync(
  587. @"class C
  588. {
  589. System.Func<int> a;
  590. int Foo()
  591. {
  592. var v = a;
  593. [||]if (v != null)
  594. {
  595. return v();
  596. }
  597. }
  598. }");
  599. }
  600. }
  601. }