| 
25 | 25 | import static au.com.integradev.delphi.symbol.resolve.EqualityType.EQUAL;  | 
26 | 26 | import static au.com.integradev.delphi.symbol.resolve.EqualityType.EXACT;  | 
27 | 27 | import static au.com.integradev.delphi.symbol.resolve.EqualityType.INCOMPATIBLE_TYPES;  | 
28 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.ANSISTRING;  | 
29 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.BYTE;  | 
30 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.CARDINAL;  | 
31 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.DOUBLE_CURRENCY;  | 
32 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.DYNAMIC_ARRAY;  | 
33 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.ENUM;  | 
34 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.EXTENDED;  | 
35 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.FORMAL_BOOLEAN;  | 
36 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.INCOMPATIBLE_VARIANT;  | 
37 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.INTEGER;  | 
38 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.NO_CONVERSION_REQUIRED;  | 
39 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.SHORTINT;  | 
40 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.SHORTSTRING;  | 
41 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.SINGLE;  | 
42 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.SMALLINT;  | 
43 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.UNICODESTRING;  | 
44 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.WIDESTRING;  | 
45 |  | -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.WORD;  | 
46 | 28 | import static java.lang.Math.abs;  | 
47 | 29 | import static java.util.function.Predicate.not;  | 
48 | 30 | 
 
  | 
 | 
52 | 34 | import com.google.common.collect.ComparisonChain;  | 
53 | 35 | import java.util.ArrayList;  | 
54 | 36 | import java.util.Collections;  | 
 | 37 | +import java.util.Comparator;  | 
55 | 38 | import java.util.List;  | 
 | 39 | +import java.util.Objects;  | 
56 | 40 | import java.util.Set;  | 
57 | 41 | import java.util.stream.Collectors;  | 
58 | 42 | import org.sonar.plugins.communitydelphi.api.type.CodePages;  | 
@@ -192,7 +176,7 @@ private static void processArgument(  | 
192 | 176 |     }  | 
193 | 177 | 
 
  | 
194 | 178 |     checkCodePageDistance(candidate, argumentType, parameterType);  | 
195 |  | -    checkVariantConversions(candidate, argumentType, parameterType);  | 
 | 179 | +    addVariantConversion(candidate, argumentType, parameterType);  | 
196 | 180 | 
 
  | 
197 | 181 |     // When an ambiguous procedural type was changed to an invocation, an exact match is  | 
198 | 182 |     // downgraded to equal.  | 
@@ -352,22 +336,15 @@ private static void checkCodePageDistance(  | 
352 | 336 |     }  | 
353 | 337 |   }  | 
354 | 338 | 
 
  | 
355 |  | -  // Keep track of implicit variant conversions  | 
356 |  | -  // Also invalidate candidates that would produce invalid variant conversions  | 
357 |  | -  private static void checkVariantConversions(  | 
 | 339 | +  private static void addVariantConversion(  | 
358 | 340 |       InvocationCandidate candidate, Type argumentType, Type parameterType) {  | 
359 |  | -    argumentType = TypeUtils.findBaseType(argumentType);  | 
360 |  | -    parameterType = TypeUtils.findBaseType(parameterType);  | 
361 |  | -    VariantConversionType variantConversionType = NO_CONVERSION_REQUIRED;  | 
362 |  | -    if (argumentType.isVariant()) {  | 
363 |  | -      variantConversionType = VariantConversionType.fromType(parameterType);  | 
364 |  | -      if (variantConversionType == INCOMPATIBLE_VARIANT) {  | 
365 |  | -        candidate.setInvalid();  | 
366 |  | -      }  | 
367 |  | -    } else if (parameterType.isVariant()) {  | 
368 |  | -      variantConversionType = VariantConversionType.fromType(argumentType);  | 
 | 341 | +    if (argumentType.isVariant() && !parameterType.isVariant()) {  | 
 | 342 | +      candidate.addVariantConversion(parameterType);  | 
 | 343 | +    } else if (parameterType.isVariant() && !argumentType.isVariant()) {  | 
 | 344 | +      candidate.addVariantConversion(argumentType);  | 
 | 345 | +    } else {  | 
 | 346 | +      candidate.addVariantConversion(null);  | 
369 | 347 |     }  | 
370 |  | -    candidate.addVariantConversion(variantConversionType);  | 
371 | 348 |   }  | 
372 | 349 | 
 
  | 
373 | 350 |   private static EqualityType varParameterAllowed(Type argType, Parameter parameter) {  | 
@@ -537,117 +514,102 @@ private int isBetterCandidate(InvocationCandidate candidate, InvocationCandidate  | 
537 | 514 |   private int getVariantDistance(InvocationCandidate candidate, InvocationCandidate bestCandidate) {  | 
538 | 515 |     int variantDistance = 0;  | 
539 | 516 |     for (int i = 0; i < arguments.size(); ++i) {  | 
540 |  | -      VariantConversionType currentVcl = candidate.getVariantConversionType(i);  | 
541 |  | -      VariantConversionType bestVcl = bestCandidate.getVariantConversionType(i);  | 
542 |  | -      variantDistance += isBetterVariantConversion(currentVcl, bestVcl);  | 
 | 517 | +      Type current = candidate.getVariantConversionType(i);  | 
 | 518 | +      Type best = bestCandidate.getVariantConversionType(i);  | 
 | 519 | +      variantDistance += isBetterVariantConversion(current, best);  | 
543 | 520 |     }  | 
544 | 521 |     return variantDistance;  | 
545 | 522 |   }  | 
546 | 523 | 
 
  | 
547 |  | -  /**  | 
548 |  | -   * Determines which variant conversion type takes precedence when converting a variant type  | 
549 |  | -   * argument to a parameter type.  | 
550 |  | -   *  | 
551 |  | -   * <p>Delphi precedence rules extracted from test programs:  | 
552 |  | -   *  | 
553 |  | -   * <ul>  | 
554 |  | -   *   <li>single > (char, currency, int64, shortstring, ansistring, widestring, unicodestring,  | 
555 |  | -   *       extended, double)  | 
556 |  | -   *   <li>double/currency > (char, int64, shortstring, ansistring, widestring, unicodestring,  | 
557 |  | -   *       extended)  | 
558 |  | -   *   <li>extended > (char, int64, shortstring, ansistring, widestring, unicodestring)  | 
559 |  | -   *   <li>longint/cardinal > (int64, shortstring, ansistring, widestring, unicodestring, extended,  | 
560 |  | -   *       double, single, char, currency)  | 
561 |  | -   *   <li>smallint > (longint, int64, shortstring, ansistring, widestring, unicodestring, extended,  | 
562 |  | -   *       double single, char, currency);  | 
563 |  | -   *   <li>word > (longint, cardinal, int64, shortstring, ansistring, widestring, unicodestring,  | 
564 |  | -   *       extended, double single, char, currency);  | 
565 |  | -   *   <li>shortint > (longint, smallint, int64, shortstring, ansistring, widestring, unicodestring,  | 
566 |  | -   *       extended, double, single, char, currency)  | 
567 |  | -   *   <li>byte > (longint, cardinal, word, smallint, int64, shortstring, ansistring, widestring,  | 
568 |  | -   *       unicodestring, extended, double, single, char, currency);  | 
569 |  | -   *   <li>boolean/formal > (char, int64, shortstring, ansistring, widestring, unicodestring)  | 
570 |  | -   *   <li>widestring > (char, int64, shortstring, ansistring, unicodestring)  | 
571 |  | -   *   <li>unicodestring > (char, int64, shortstring, ansistring)  | 
572 |  | -   *   <li>ansistring > (char, int64, shortstring)  | 
573 |  | -   *   <li>shortstring > (char, int64)  | 
574 |  | -   * </ul>  | 
575 |  | -   *  | 
576 |  | -   * <p>Relations not mentioned mean that they conflict: no decision possible  | 
577 |  | -   *  | 
578 |  | -   * @param currentVcl The conversion type we're checking  | 
579 |  | -   * @param bestVcl The best conversion type so far  | 
580 |  | -   * @return  | 
581 |  | -   *     <ul>  | 
582 |  | -   *       <li>> 0 when currentVcl is better than bestVcl  | 
583 |  | -   *       <li>< 0 when bestVcl is better than currentVcl  | 
584 |  | -   *       <li>= 0 when both are equal  | 
585 |  | -   *     </ul>  | 
586 |  | -   *  | 
587 |  | -   * @see <a href="https://github.com/fpc/FPCSource/blob/main/compiler/htypechk.pas#L3367">  | 
588 |  | -   *     is_better_candidate_single_variant</a>  | 
589 |  | -   */  | 
590 |  | -  private static int isBetterVariantConversion(  | 
591 |  | -      VariantConversionType currentVcl, VariantConversionType bestVcl) {  | 
592 |  | -    if (currentVcl == bestVcl) {  | 
 | 524 | +  private static int isBetterVariantConversion(Type current, Type best) {  | 
 | 525 | +    if (current == null && best == null) {  | 
593 | 526 |       return 0;  | 
594 |  | -    } else if (currentVcl == INCOMPATIBLE_VARIANT || bestVcl == NO_CONVERSION_REQUIRED) {  | 
595 |  | -      return -1;  | 
596 |  | -    } else if (bestVcl == INCOMPATIBLE_VARIANT || currentVcl == NO_CONVERSION_REQUIRED) {  | 
597 |  | -      return 1;  | 
598 |  | -    } else if (currentVcl == FORMAL_BOOLEAN || bestVcl == FORMAL_BOOLEAN) {  | 
599 |  | -      if (currentVcl == FORMAL_BOOLEAN) {  | 
600 |  | -        return VariantConversionType.isChari64Str(bestVcl) ? 1 : 0;  | 
601 |  | -      } else {  | 
602 |  | -        return VariantConversionType.isChari64Str(currentVcl) ? -1 : 0;  | 
603 |  | -      }  | 
604 |  | -    } else if (currentVcl == BYTE || bestVcl == BYTE) {  | 
605 |  | -      return calculateRelation(currentVcl, bestVcl, BYTE, Set.of(SHORTINT));  | 
606 |  | -    } else if (currentVcl == SHORTINT || bestVcl == SHORTINT) {  | 
607 |  | -      return calculateRelation(currentVcl, bestVcl, SHORTINT, Set.of(WORD, CARDINAL));  | 
608 |  | -    } else if (currentVcl == WORD || bestVcl == WORD) {  | 
609 |  | -      return calculateRelation(currentVcl, bestVcl, WORD, Set.of(SMALLINT));  | 
610 |  | -    } else if (currentVcl == SMALLINT || bestVcl == SMALLINT) {  | 
611 |  | -      return calculateRelation(currentVcl, bestVcl, SMALLINT, Set.of(CARDINAL));  | 
612 |  | -    } else if (currentVcl == CARDINAL || bestVcl == CARDINAL) {  | 
613 |  | -      return calculateRelation(currentVcl, bestVcl, CARDINAL, Set.of(INTEGER));  | 
614 |  | -    } else if (currentVcl == INTEGER || bestVcl == INTEGER) {  | 
615 |  | -      return (bestVcl == INTEGER) ? -1 : 1;  | 
616 |  | -    } else if (currentVcl == SINGLE || bestVcl == SINGLE) {  | 
617 |  | -      return (bestVcl == SINGLE) ? -1 : 1;  | 
618 |  | -    } else if (currentVcl == DOUBLE_CURRENCY || bestVcl == DOUBLE_CURRENCY) {  | 
619 |  | -      return (bestVcl == DOUBLE_CURRENCY) ? -1 : 1;  | 
620 |  | -    } else if (currentVcl == EXTENDED || bestVcl == EXTENDED) {  | 
621 |  | -      return (bestVcl == EXTENDED) ? -1 : 1;  | 
622 |  | -    } else if (currentVcl == WIDESTRING || bestVcl == WIDESTRING) {  | 
623 |  | -      return (bestVcl == WIDESTRING) ? -1 : 1;  | 
624 |  | -    } else if (currentVcl == UNICODESTRING || bestVcl == UNICODESTRING) {  | 
625 |  | -      return (bestVcl == UNICODESTRING) ? -1 : 1;  | 
626 |  | -    } else if (currentVcl == ANSISTRING || bestVcl == ANSISTRING) {  | 
627 |  | -      return (bestVcl == ANSISTRING) ? -1 : 1;  | 
628 |  | -    } else if (currentVcl == SHORTSTRING || bestVcl == SHORTSTRING) {  | 
629 |  | -      return (bestVcl == SHORTSTRING) ? -1 : 1;  | 
630 |  | -    } else if (currentVcl == ENUM || bestVcl == ENUM) {  | 
631 |  | -      return (bestVcl == ENUM) ? -1 : 1;  | 
632 |  | -    } else if (currentVcl == DYNAMIC_ARRAY || bestVcl == DYNAMIC_ARRAY) {  | 
633 |  | -      return (bestVcl == DYNAMIC_ARRAY) ? -1 : 1;  | 
634 |  | -    }  | 
635 |  | - | 
636 |  | -    // All possibilities should have been checked now.  | 
637 |  | -    throw new AssertionError("Unhandled VariantConversionType!");  | 
 | 527 | +    }  | 
 | 528 | +    return ComparisonChain.start()  | 
 | 529 | +        .compare(current, best, Comparator.comparing(Objects::isNull))  | 
 | 530 | +        .compare(current, best, Comparator.comparing(InvocationResolver::isIInterface))  | 
 | 531 | +        .compare(current, best, Comparator.comparing(Type::isUntyped))  | 
 | 532 | +        .compare(current, best, InvocationResolver::compareNumericType)  | 
 | 533 | +        .compare(current, best, InvocationResolver::compareRealSize)  | 
 | 534 | +        .compare(current, best, InvocationResolver::compareIntegerRange)  | 
 | 535 | +        .compare(current, best, InvocationResolver::compareStringType)  | 
 | 536 | +        .result();  | 
638 | 537 |   }  | 
639 | 538 | 
 
  | 
640 |  | -  private static int calculateRelation(  | 
641 |  | -      VariantConversionType currentVcl,  | 
642 |  | -      VariantConversionType bestVcl,  | 
643 |  | -      VariantConversionType testVcl,  | 
644 |  | -      Set<VariantConversionType> conflictTypes) {  | 
645 |  | -    if (conflictTypes.contains(bestVcl) || conflictTypes.contains(currentVcl)) {  | 
646 |  | -      return 0;  | 
647 |  | -    } else if (bestVcl == testVcl) {  | 
 | 539 | +  private static boolean isIInterface(Type type) {  | 
 | 540 | +    return TypeUtils.findBaseType(type).is("System.IInterface");  | 
 | 541 | +  }  | 
 | 542 | + | 
 | 543 | +  private static int compareNumericType(Type a, Type b) {  | 
 | 544 | +    if (a.isReal() && b.isInteger()) {  | 
 | 545 | +      return 1;  | 
 | 546 | +    } else if (b.isReal() && a.isInteger()) {  | 
648 | 547 |       return -1;  | 
649 | 548 |     } else {  | 
650 |  | -      return 1;  | 
 | 549 | +      return 0;  | 
 | 550 | +    }  | 
 | 551 | +  }  | 
 | 552 | + | 
 | 553 | +  private static int compareRealSize(Type a, Type b) {  | 
 | 554 | +    if (!a.isReal() || !b.isReal()) {  | 
 | 555 | +      return 0;  | 
 | 556 | +    }  | 
 | 557 | +    if (isCurrencyCompConflict(a, b) || isCurrencyCompConflict(b, a)) {  | 
 | 558 | +      return 0;  | 
 | 559 | +    }  | 
 | 560 | +    return Objects.compare(a, b, Comparator.comparingInt(Type::size));  | 
 | 561 | +  }  | 
 | 562 | + | 
 | 563 | +  private static boolean isCurrencyCompConflict(Type currencyComp, Type real) {  | 
 | 564 | +    currencyComp = TypeUtils.findBaseType(currencyComp);  | 
 | 565 | +    return (currencyComp.is(IntrinsicType.CURRENCY) || currencyComp.is(IntrinsicType.COMP))  | 
 | 566 | +        && real.isReal()  | 
 | 567 | +        && currencyComp.size() >= real.size();  | 
 | 568 | +  }  | 
 | 569 | + | 
 | 570 | +  private static int compareIntegerRange(Type a, Type b) {  | 
 | 571 | +    if (!a.isInteger() || !b.isInteger()) {  | 
 | 572 | +      return 0;  | 
651 | 573 |     }  | 
 | 574 | + | 
 | 575 | +    IntegerType intA = (IntegerType) a;  | 
 | 576 | +    IntegerType intB = (IntegerType) b;  | 
 | 577 | + | 
 | 578 | +    if (valueRangesAreAmbiguous(intA, intB)) {  | 
 | 579 | +      return 0;  | 
 | 580 | +    }  | 
 | 581 | + | 
 | 582 | +    return ComparisonChain.start()  | 
 | 583 | +        .compare(intA, intB, Comparator.comparing(IntegerType::max))  | 
 | 584 | +        .compare(intB, intA, Comparator.comparing(IntegerType::min))  | 
 | 585 | +        .result();  | 
 | 586 | +  }  | 
 | 587 | + | 
 | 588 | +  private static boolean valueRangesAreAmbiguous(IntegerType a, IntegerType b) {  | 
 | 589 | +    return a.isSigned() == b.isSigned()  | 
 | 590 | +        && !(a.min().compareTo(b.min()) <= 0 && a.max().compareTo(b.max()) >= 0)  | 
 | 591 | +        && !(b.min().compareTo(a.min()) <= 0 && b.max().compareTo(a.max()) >= 0);  | 
 | 592 | +  }  | 
 | 593 | + | 
 | 594 | +  private static int compareStringType(Type a, Type b) {  | 
 | 595 | +    if (!a.isString() || !b.isString()) {  | 
 | 596 | +      return 0;  | 
 | 597 | +    }  | 
 | 598 | + | 
 | 599 | +    return Comparator.<Type>comparingInt(  | 
 | 600 | +            type -> {  | 
 | 601 | +              type = TypeUtils.findBaseType(type);  | 
 | 602 | +              if (type.is(IntrinsicType.WIDESTRING)) {  | 
 | 603 | +                return 1;  | 
 | 604 | +              } else if (type.is(IntrinsicType.UNICODESTRING)) {  | 
 | 605 | +                return 2;  | 
 | 606 | +              } else if (type.isAnsiString()) {  | 
 | 607 | +                return 3;  | 
 | 608 | +              } else {  | 
 | 609 | +                return 4;  | 
 | 610 | +              }  | 
 | 611 | +            })  | 
 | 612 | +        .reversed()  | 
 | 613 | +        .compare(a, b);  | 
652 | 614 |   }  | 
653 | 615 | }  | 
0 commit comments