@@ -648,30 +648,47 @@ using GenerateFn = llvm::function_ref<void(
648
648
CompilerInvocation &, SmallVectorImpl<const char *> &,
649
649
CompilerInvocation::StringAllocator)>;
650
650
651
- // May perform round-trip of command line arguments. By default, the round-trip
652
- // is enabled in assert builds. This can be overwritten at run-time via the
653
- // "-round-trip-args" and "-no-round-trip-args" command line flags.
654
- // During round-trip, the command line arguments are parsed into a dummy
655
- // instance of CompilerInvocation which is used to generate the command line
656
- // arguments again. The real CompilerInvocation instance is then created by
657
- // parsing the generated arguments, not the original ones.
651
+ // / May perform round-trip of command line arguments. By default, the round-trip
652
+ // / is enabled in assert builds. This can be overwritten at run-time via the
653
+ // / "-round-trip-args" and "-no-round-trip-args" command line flags, or via the
654
+ // / ForceRoundTrip parameter.
655
+ // /
656
+ // / During round-trip, the command line arguments are parsed into a dummy
657
+ // / CompilerInvocation, which is used to generate the command line arguments
658
+ // / again. The real CompilerInvocation is then created by parsing the generated
659
+ // / arguments, not the original ones. This (in combination with tests covering
660
+ // / argument behavior) ensures the generated command line is complete (doesn't
661
+ // / drop/mangle any arguments).
662
+ // /
663
+ // / Finally, we check the command line that was used to create the real
664
+ // / CompilerInvocation instance. By default, we compare it to the command line
665
+ // / the real CompilerInvocation generates. This checks whether the generator is
666
+ // / deterministic. If \p CheckAgainstOriginalInvocation is enabled, we instead
667
+ // / compare it to the original command line to verify the original command-line
668
+ // / was canonical and can round-trip exactly.
658
669
static bool RoundTrip (ParseFn Parse, GenerateFn Generate,
659
670
CompilerInvocation &RealInvocation,
660
671
CompilerInvocation &DummyInvocation,
661
672
ArrayRef<const char *> CommandLineArgs,
662
- DiagnosticsEngine &Diags, const char *Argv0) {
673
+ DiagnosticsEngine &Diags, const char *Argv0,
674
+ bool CheckAgainstOriginalInvocation = false ,
675
+ bool ForceRoundTrip = false ) {
663
676
#ifndef NDEBUG
664
677
bool DoRoundTripDefault = true ;
665
678
#else
666
679
bool DoRoundTripDefault = false ;
667
680
#endif
668
681
669
682
bool DoRoundTrip = DoRoundTripDefault;
670
- for (const auto *Arg : CommandLineArgs) {
671
- if (Arg == StringRef (" -round-trip-args" ))
672
- DoRoundTrip = true ;
673
- if (Arg == StringRef (" -no-round-trip-args" ))
674
- DoRoundTrip = false ;
683
+ if (ForceRoundTrip) {
684
+ DoRoundTrip = true ;
685
+ } else {
686
+ for (const auto *Arg : CommandLineArgs) {
687
+ if (Arg == StringRef (" -round-trip-args" ))
688
+ DoRoundTrip = true ;
689
+ if (Arg == StringRef (" -no-round-trip-args" ))
690
+ DoRoundTrip = false ;
691
+ }
675
692
}
676
693
677
694
// If round-trip was not requested, simply run the parser with the real
@@ -726,30 +743,34 @@ static bool RoundTrip(ParseFn Parse, GenerateFn Generate,
726
743
// Generate arguments from the dummy invocation. If Generate is the
727
744
// inverse of Parse, the newly generated arguments must have the same
728
745
// semantics as the original.
729
- SmallVector<const char *> GeneratedArgs1 ;
730
- Generate (DummyInvocation, GeneratedArgs1 , SA);
746
+ SmallVector<const char *> GeneratedArgs ;
747
+ Generate (DummyInvocation, GeneratedArgs , SA);
731
748
732
749
// Run the second parse, now on the generated arguments, and with the real
733
750
// invocation and diagnostics. The result is what we will end up using for the
734
751
// rest of compilation, so if Generate is not inverse of Parse, something down
735
752
// the line will break.
736
- bool Success2 = Parse (RealInvocation, GeneratedArgs1 , Diags, Argv0);
753
+ bool Success2 = Parse (RealInvocation, GeneratedArgs , Diags, Argv0);
737
754
738
755
// The first parse on original arguments succeeded, but second parse of
739
756
// generated arguments failed. Something must be wrong with the generator.
740
757
if (!Success2) {
741
758
Diags.Report (diag::err_cc1_round_trip_ok_then_fail);
742
759
Diags.Report (diag::note_cc1_round_trip_generated)
743
- << 1 << SerializeArgs (GeneratedArgs1 );
760
+ << 1 << SerializeArgs (GeneratedArgs );
744
761
return false ;
745
762
}
746
763
747
- // Generate arguments again, this time from the options we will end up using
748
- // for the rest of the compilation.
749
- SmallVector<const char *> GeneratedArgs2;
750
- Generate (RealInvocation, GeneratedArgs2, SA);
764
+ SmallVector<const char *> ComparisonArgs;
765
+ if (CheckAgainstOriginalInvocation)
766
+ // Compare against original arguments.
767
+ ComparisonArgs.assign (CommandLineArgs.begin (), CommandLineArgs.end ());
768
+ else
769
+ // Generate arguments again, this time from the options we will end up using
770
+ // for the rest of the compilation.
771
+ Generate (RealInvocation, ComparisonArgs, SA);
751
772
752
- // Compares two lists of generated arguments.
773
+ // Compares two lists of arguments.
753
774
auto Equal = [](const ArrayRef<const char *> A,
754
775
const ArrayRef<const char *> B) {
755
776
return std::equal (A.begin (), A.end (), B.begin (), B.end (),
@@ -761,23 +782,41 @@ static bool RoundTrip(ParseFn Parse, GenerateFn Generate,
761
782
// If we generated different arguments from what we assume are two
762
783
// semantically equivalent CompilerInvocations, the Generate function may
763
784
// be non-deterministic.
764
- if (!Equal (GeneratedArgs1, GeneratedArgs2 )) {
785
+ if (!Equal (GeneratedArgs, ComparisonArgs )) {
765
786
Diags.Report (diag::err_cc1_round_trip_mismatch);
766
787
Diags.Report (diag::note_cc1_round_trip_generated)
767
- << 1 << SerializeArgs (GeneratedArgs1 );
788
+ << 1 << SerializeArgs (GeneratedArgs );
768
789
Diags.Report (diag::note_cc1_round_trip_generated)
769
- << 2 << SerializeArgs (GeneratedArgs2 );
790
+ << 2 << SerializeArgs (ComparisonArgs );
770
791
return false ;
771
792
}
772
793
773
794
Diags.Report (diag::remark_cc1_round_trip_generated)
774
- << 1 << SerializeArgs (GeneratedArgs1 );
795
+ << 1 << SerializeArgs (GeneratedArgs );
775
796
Diags.Report (diag::remark_cc1_round_trip_generated)
776
- << 2 << SerializeArgs (GeneratedArgs2 );
797
+ << 2 << SerializeArgs (ComparisonArgs );
777
798
778
799
return Success2;
779
800
}
780
801
802
+ bool CompilerInvocation::checkCC1RoundTrip (ArrayRef<const char *> Args,
803
+ DiagnosticsEngine &Diags,
804
+ const char *Argv0) {
805
+ CompilerInvocation DummyInvocation1, DummyInvocation2;
806
+ return RoundTrip (
807
+ [](CompilerInvocation &Invocation, ArrayRef<const char *> CommandLineArgs,
808
+ DiagnosticsEngine &Diags, const char *Argv0) {
809
+ return CreateFromArgsImpl (Invocation, CommandLineArgs, Diags, Argv0);
810
+ },
811
+ [](CompilerInvocation &Invocation, SmallVectorImpl<const char *> &Args,
812
+ StringAllocator SA) {
813
+ Args.push_back (" -cc1" );
814
+ Invocation.generateCC1CommandLine (Args, SA);
815
+ },
816
+ DummyInvocation1, DummyInvocation2, Args, Diags, Argv0,
817
+ /* CheckAgainstOriginalInvocation=*/ true , /* ForceRoundTrip=*/ true );
818
+ }
819
+
781
820
static void addDiagnosticArgs (ArgList &Args, OptSpecifier Group,
782
821
OptSpecifier GroupWithValue,
783
822
std::vector<std::string> &Diagnostics) {
0 commit comments