|
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