|
4 | 4 |
|
5 | 5 | import csharp
|
6 | 6 | private import DataFlow
|
| 7 | +private import semmle.code.csharp.commons.QualifiedName |
7 | 8 | private import semmle.code.csharp.frameworks.System
|
8 | 9 | private import semmle.code.csharp.frameworks.system.data.Entity
|
9 | 10 | private import semmle.code.csharp.frameworks.system.collections.Generic
|
@@ -236,7 +237,7 @@ module EntityFramework {
|
236 | 237 | * }
|
237 | 238 | * ```
|
238 | 239 | */
|
239 |
| - private Property getADbSetProperty(Class elementType) { |
| 240 | + Property getADbSetProperty(Class elementType) { |
240 | 241 | exists(ConstructedClass c |
|
241 | 242 | result.getType() = c and
|
242 | 243 | c.getUnboundDeclaration() instanceof DbSet and
|
@@ -351,67 +352,137 @@ module EntityFramework {
|
351 | 352 |
|
352 | 353 | /** Holds if component stack `head :: tail` is required for the output specification. */
|
353 | 354 | predicate requiresComponentStackOut(
|
354 |
| - Content head, Type headType, SummaryComponentStack tail, int dist |
| 355 | + Content head, Type headType, SummaryComponentStack tail, int dist, |
| 356 | + DbContextClassSetProperty dbSetProp |
355 | 357 | ) {
|
356 |
| - exists(Property dbSetProp, PropertyContent c1 | |
| 358 | + exists(PropertyContent c1 | |
357 | 359 | dbSetProp = this.getADbSetProperty(headType) and
|
358 | 360 | this.stepRev(c1, _, head, headType, 0) and
|
359 | 361 | c1.getProperty() = dbSetProp and
|
360 |
| - tail = SummaryComponentStack::jump(dbSetProp.getGetter()) and |
| 362 | + tail = SummaryComponentStack::return() and |
361 | 363 | dist = 0
|
362 | 364 | )
|
363 | 365 | or
|
364 | 366 | exists(Content tailHead, SummaryComponentStack tailTail, Type tailType |
|
365 |
| - this.requiresComponentStackOut(tailHead, tailType, tailTail, dist - 1) and |
| 367 | + this.requiresComponentStackOut(tailHead, tailType, tailTail, dist - 1, dbSetProp) and |
366 | 368 | tail = SummaryComponentStack::push(SummaryComponent::content(tailHead), tailTail) and
|
367 | 369 | this.stepRev(tailHead, tailType, head, headType, dist)
|
368 | 370 | )
|
369 | 371 | }
|
370 |
| - } |
371 |
| - |
372 |
| - private class DbContextSaveChanges extends EFSummarizedCallable { |
373 |
| - private DbContextClass c; |
374 |
| - |
375 |
| - DbContextSaveChanges() { this = c.getASaveChanges() } |
376 | 372 |
|
| 373 | + /** |
| 374 | + * Holds if `input` is a valid summary component stack for property `mapped` for this. |
| 375 | + */ |
377 | 376 | pragma[noinline]
|
378 |
| - private predicate input(SummaryComponentStack input, Property mapped) { |
| 377 | + predicate input(SummaryComponentStack input, Property mapped) { |
379 | 378 | exists(PropertyContent head, SummaryComponentStack tail |
|
380 |
| - c.requiresComponentStackIn(head, _, tail, _) and |
| 379 | + this.requiresComponentStackIn(head, _, tail, _) and |
381 | 380 | head.getProperty() = mapped and
|
382 |
| - mapped = c.getAColumnProperty(_) and |
| 381 | + mapped = this.getAColumnProperty(_) and |
383 | 382 | input = SummaryComponentStack::push(SummaryComponent::content(head), tail)
|
384 | 383 | )
|
385 | 384 | }
|
386 | 385 |
|
| 386 | + /** |
| 387 | + * Holds if `output` is a valid summary component stack for the getter of `dbSet` |
| 388 | + * for property `mapped` for this. |
| 389 | + */ |
387 | 390 | pragma[noinline]
|
388 |
| - private predicate output(SummaryComponentStack output, Property mapped) { |
| 391 | + private predicate output( |
| 392 | + SummaryComponentStack output, Property mapped, DbContextClassSetProperty dbSet |
| 393 | + ) { |
389 | 394 | exists(PropertyContent head, SummaryComponentStack tail |
|
390 |
| - c.requiresComponentStackOut(head, _, tail, _) and |
| 395 | + this.requiresComponentStackOut(head, _, tail, _, dbSet) and |
391 | 396 | head.getProperty() = mapped and
|
392 |
| - mapped = c.getAColumnProperty(_) and |
| 397 | + mapped = this.getAColumnProperty(_) and |
393 | 398 | output = SummaryComponentStack::push(SummaryComponent::content(head), tail)
|
394 | 399 | )
|
395 | 400 | }
|
396 | 401 |
|
| 402 | + /** |
| 403 | + * Gets the synthetic name for the getter of `dbSet` for property `mapped` for this, |
| 404 | + * where `output` is a valid summary component stack for the getter of `dbSet` |
| 405 | + * for the property `mapped`. |
| 406 | + */ |
| 407 | + pragma[nomagic] |
| 408 | + string getSyntheticName( |
| 409 | + SummaryComponentStack output, Property mapped, DbContextClassSetProperty dbSet |
| 410 | + ) { |
| 411 | + this = dbSet.getDbContextClass() and |
| 412 | + this.output(output, mapped, dbSet) and |
| 413 | + result = dbSet.getFullName() + "#" + SummaryComponentStack::getComponentStack(output) |
| 414 | + } |
| 415 | + } |
| 416 | + |
| 417 | + private class DbContextClassSetProperty extends Property { |
| 418 | + private DbContextClass c; |
| 419 | + |
| 420 | + DbContextClassSetProperty() { this = c.getADbSetProperty(_) } |
| 421 | + |
| 422 | + /** |
| 423 | + * Gets the fully qualified name for this. |
| 424 | + */ |
| 425 | + string getFullName() { |
| 426 | + exists(string qualifier, string type, string name | |
| 427 | + this.hasQualifiedName(qualifier, type, name) |
| 428 | + | |
| 429 | + result = getQualifiedName(qualifier, type, name) |
| 430 | + ) |
| 431 | + } |
| 432 | + |
| 433 | + /** |
| 434 | + * Gets the context class where this is a DbSet property. |
| 435 | + */ |
| 436 | + DbContextClass getDbContextClass() { result = c } |
| 437 | + } |
| 438 | + |
| 439 | + private class DbContextClassSetPropertySynthetic extends EFSummarizedCallable { |
| 440 | + private DbContextClassSetProperty p; |
| 441 | + |
| 442 | + DbContextClassSetPropertySynthetic() { this = p.getGetter() } |
| 443 | + |
397 | 444 | override predicate propagatesFlow(
|
398 | 445 | SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
399 | 446 | ) {
|
400 |
| - exists(Property mapped | |
| 447 | + exists(string name, DbContextClass c | |
401 | 448 | preservesValue = true and
|
402 |
| - this.input(input, mapped) and |
403 |
| - this.output(output, mapped) |
| 449 | + name = c.getSyntheticName(output, _, p) and |
| 450 | + input = SummaryComponentStack::syntheticGlobal(name) |
404 | 451 | )
|
405 | 452 | }
|
406 | 453 | }
|
407 | 454 |
|
| 455 | + private class DbContextSaveChanges extends EFSummarizedCallable { |
| 456 | + private DbContextClass c; |
| 457 | + |
| 458 | + DbContextSaveChanges() { this = c.getASaveChanges() } |
| 459 | + |
| 460 | + override predicate propagatesFlow( |
| 461 | + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue |
| 462 | + ) { |
| 463 | + exists(string name, Property mapped | |
| 464 | + preservesValue = true and |
| 465 | + c.input(input, mapped) and |
| 466 | + name = c.getSyntheticName(_, mapped, _) and |
| 467 | + output = SummaryComponentStack::syntheticGlobal(name) |
| 468 | + ) |
| 469 | + } |
| 470 | + } |
| 471 | + |
| 472 | + /** |
| 473 | + * Add all possible synthetic global names. |
| 474 | + */ |
| 475 | + private class EFSummarizedCallableSyntheticGlobal extends SummaryComponent::SyntheticGlobal { |
| 476 | + EFSummarizedCallableSyntheticGlobal() { this = any(DbContextClass c).getSyntheticName(_, _, _) } |
| 477 | + } |
| 478 | + |
408 | 479 | private class DbContextSaveChangesRequiredSummaryComponentStack extends RequiredSummaryComponentStack
|
409 | 480 | {
|
410 | 481 | override predicate required(SummaryComponent head, SummaryComponentStack tail) {
|
411 | 482 | exists(Content c | head = SummaryComponent::content(c) |
|
412 | 483 | any(DbContextClass cls).requiresComponentStackIn(c, _, tail, _)
|
413 | 484 | or
|
414 |
| - any(DbContextClass cls).requiresComponentStackOut(c, _, tail, _) |
| 485 | + any(DbContextClass cls).requiresComponentStackOut(c, _, tail, _, _) |
415 | 486 | )
|
416 | 487 | }
|
417 | 488 | }
|
|
0 commit comments