main
cs 166 lines 8.08 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core.Link
4 {
5 using System.Collections.Generic;
6 using System.Linq;
7 using WixToolset.Data;
8 using WixToolset.Data.Symbols;
9 using WixToolset.Extensibility.Services;
10
11 internal class ProcessConflictingSymbolsCommand
12 {
13 public ProcessConflictingSymbolsCommand(IMessaging messaging, IReadOnlyCollection<SymbolWithSection> possibleConflicts, IReadOnlyCollection<SymbolWithSection> overrideSymbols, ISet<IntermediateSection> resolvedSections)
14 {
15 this.Messaging = messaging;
16 this.PossibleConflicts = possibleConflicts;
17 this.OverrideSymbols = overrideSymbols;
18 this.ResolvedSections = resolvedSections;
19 }
20
21 private IMessaging Messaging { get; }
22
23 private IReadOnlyCollection<SymbolWithSection> PossibleConflicts { get; }
24
25 private ISet<IntermediateSection> ResolvedSections { get; }
26
27 private IReadOnlyCollection<SymbolWithSection> OverrideSymbols { get; }
28
29 /// <summary>
30 /// Gets the collection of overridden symbols that should not be included
31 /// in the final output.
32 /// </summary>
33 public ISet<IntermediateSymbol> OverriddenSymbols { get; private set; }
34
35 public void Execute()
36 {
37 var overriddenSymbols = new HashSet<IntermediateSymbol>();
38
39 foreach (var symbolWithConflicts in this.PossibleConflicts)
40 {
41 var conflicts = YieldReferencedConflicts(symbolWithConflicts, this.ResolvedSections).ToList();
42
43 if (conflicts.Count > 1)
44 {
45 IEnumerable<SymbolWithSection> reportDuplicates;
46
47 var virtualConflicts = conflicts.Where(s => s.Access == AccessModifier.Virtual).ToList();
48
49 // No virtual symbols, just plain old duplicate errors. This is the easy case.
50 if (virtualConflicts.Count == 0)
51 {
52 var first = conflicts[0];
53 reportDuplicates = conflicts.Skip(1);
54
55 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
56
57 this.Messaging.Write(LinkerErrors.DuplicateSymbol(first.Symbol, referencingSourceLineNumber));
58 }
59 else // there are virtual symbols, which complicates conflict resolution and may not be an error at all.
60 {
61 var firstVirtualSymbol = virtualConflicts[0];
62 var overrideConflicts = conflicts.Where(s => s.Access == AccessModifier.Override).ToList();
63
64 // If there is a single virtual symbol, there may be a single override symbol to make this a success case.
65 // All other scenarios are errors.
66 if (virtualConflicts.Count == 1)
67 {
68 var otherConflicts = conflicts.Where(s => s.Access != AccessModifier.Virtual && s.Access != AccessModifier.Override).ToList();
69
70 if (otherConflicts.Count > 0)
71 {
72 var first = otherConflicts[0];
73 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
74
75 reportDuplicates = virtualConflicts;
76
77 switch (first.Symbol)
78 {
79 case WixActionSymbol action:
80 this.Messaging.Write(LinkerErrors.VirtualSymbolMustBeOverridden(action));
81 break;
82 default:
83 this.Messaging.Write(LinkerErrors.VirtualSymbolMustBeOverridden(first.Symbol, referencingSourceLineNumber));
84 break;
85 }
86 }
87 else if (overrideConflicts.Count > 1) // multiple overrides report as normal duplicates.
88 {
89 var first = overrideConflicts[0];
90 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
91
92 reportDuplicates = overrideConflicts.Skip(1);
93
94 this.Messaging.Write(LinkerErrors.DuplicateSymbol(first.Symbol, referencingSourceLineNumber));
95 }
96 else // the single virtual symbol is overridden by a single override symbol. This is a success case.
97 {
98 var overrideSymbol = overrideConflicts[0];
99
100 overriddenSymbols.Add(firstVirtualSymbol.Symbol);
101
102 reportDuplicates = Enumerable.Empty<SymbolWithSection>();
103 }
104 }
105 else // multiple symbols are virtual, use the duplicate virtual symbol message.
106 {
107 var first = virtualConflicts[0];
108 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
109
110 reportDuplicates = virtualConflicts.Skip(1);
111
112 this.Messaging.Write(LinkerErrors.DuplicateVirtualSymbol(first.Symbol, referencingSourceLineNumber));
113 }
114
115 // Always point the override symbols at the first virtual symbol to prevent error being reported about missing overrides.
116 // There may have been errors reported above, but there was at least one virtual symbol to satisfy the overrides so we
117 // don't want extra errors in this case.
118 foreach (var overrideSymbol in overrideConflicts)
119 {
120 overrideSymbol.OverrideVirtualSymbol(firstVirtualSymbol);
121 }
122 }
123
124 foreach (var duplicate in reportDuplicates)
125 {
126 this.Messaging.Write(LinkerErrors.DuplicateSymbol2(duplicate.Symbol));
127 }
128 }
129 }
130
131 // Ensure referenced override symbols actually overrode a virtual symbol.
132 foreach (var referencedOverrideSymbol in this.OverrideSymbols.Where(s => this.ResolvedSections.Contains(s.Section)))
133 {
134 // The easiest check is to see if the symbol overrode a virtual symbol. If not, check to see if there were any possible
135 // virtual symbols that could have been overridden. If not, then we have an error.
136 if (referencedOverrideSymbol.Overrides is null)
137 {
138 var otherVirtualsCount = referencedOverrideSymbol.PossiblyConflicts.Count(s => s.Access == AccessModifier.Virtual);
139
140 if (otherVirtualsCount == 0)
141 {
142 this.Messaging.Write(LinkerErrors.VirtualSymbolNotFoundForOverride(referencedOverrideSymbol.Symbol));
143 }
144 }
145 }
146
147 this.OverriddenSymbols = overriddenSymbols;
148 }
149
150 private static IEnumerable<SymbolWithSection> YieldReferencedConflicts(SymbolWithSection symbolWithConflicts, ISet<IntermediateSection> resolvedSections)
151 {
152 if (resolvedSections.Contains(symbolWithConflicts.Section))
153 {
154 yield return symbolWithConflicts;
155 }
156
157 foreach (var possibleConflict in symbolWithConflicts.PossiblyConflicts)
158 {
159 if (resolvedSections.Contains(possibleConflict.Section))
160 {
161 yield return possibleConflict;
162 }
163 }
164 }
165 }
166 }