aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2023-12-16 09:03:44 -0800
committerRob Mensching <rob@firegiant.com>2023-12-16 14:32:21 -0800
commit0bf10d9873577f8c943cae1f50dedf68565847ee (patch)
treef62be6f9a0909bde26eda675ad48e08c48d1e8e2
parenteff9d4204880535f821827e5aece5068402358f6 (diff)
downloadwix-0bf10d9873577f8c943cae1f50dedf68565847ee.tar.gz
wix-0bf10d9873577f8c943cae1f50dedf68565847ee.tar.bz2
wix-0bf10d9873577f8c943cae1f50dedf68565847ee.zip
Improve error reporting of duplicate symbols
Virtual symbols provide more interesting ways to have (and avoid) conflicts. Adding additional messages and cleaning up the existing messages should help users know what options they have to address conflicts. This also puts all the conflict resolution in ReportConflictingSymbolsCommand instead of spreading it across reference resolution as well.
-rw-r--r--src/api/wix/WixToolset.Data/ErrorMessages.cs17
-rw-r--r--src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs6
-rw-r--r--src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs61
-rw-r--r--src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs57
-rw-r--r--src/wix/WixToolset.Core/Link/SymbolWithSection.cs21
-rw-r--r--src/wix/WixToolset.Core/LinkerErrors.cs68
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/AccessModifierFixture.cs41
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/RegistryFixture.cs14
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/RollbackBoundaryFixture.cs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicatePublicOverrideVirtualSymbol.wxs17
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicatedVirtualSymbol.wxs13
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/VirtualSymbolWithoutOverride.wxs12
12 files changed, 251 insertions, 78 deletions
diff --git a/src/api/wix/WixToolset.Data/ErrorMessages.cs b/src/api/wix/WixToolset.Data/ErrorMessages.cs
index 889d1762..e149d54a 100644
--- a/src/api/wix/WixToolset.Data/ErrorMessages.cs
+++ b/src/api/wix/WixToolset.Data/ErrorMessages.cs
@@ -323,21 +323,6 @@ namespace WixToolset.Data
323 return Message(null, Ids.DuplicateSourcesForOutput, "Multiple source files ({0}) have resulted in the same output file '{1}'. This is likely because the source files only differ in extension or path. Rename the source files to avoid this problem.", sourceList, outputFile); 323 return Message(null, Ids.DuplicateSourcesForOutput, "Multiple source files ({0}) have resulted in the same output file '{1}'. This is likely because the source files only differ in extension or path. Rename the source files to avoid this problem.", sourceList, outputFile);
324 } 324 }
325 325
326 public static Message DuplicateSymbol(SourceLineNumber sourceLineNumbers, string symbolName)
327 {
328 return Message(sourceLineNumbers, Ids.DuplicateSymbol, "Duplicate symbol '{0}' found. This typically means that an Id is duplicated. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.", symbolName);
329 }
330
331 public static Message DuplicateSymbol(SourceLineNumber sourceLineNumbers, string symbolName, string referencingSourceLineNumber)
332 {
333 return Message(sourceLineNumbers, Ids.DuplicateSymbol, "Duplicate symbol '{0}' referenced by {1}. This typically means that an Id is duplicated. Ensure all your identifiers of a given type (Directory, File, etc.) are unique or use an access modifier to scope the identfier.", symbolName, referencingSourceLineNumber);
334 }
335
336 public static Message DuplicateSymbol2(SourceLineNumber sourceLineNumbers)
337 {
338 return Message(sourceLineNumbers, Ids.DuplicateSymbol2, "Location of symbol related to previous error.");
339 }
340
341 public static Message DuplicateTransform(string transform) 326 public static Message DuplicateTransform(string transform)
342 { 327 {
343 return Message(null, Ids.DuplicateTransform, "The transform {0} was included twice on the command line. Each transform can be applied to a patch only once.", transform); 328 return Message(null, Ids.DuplicateTransform, "The transform {0} was included twice on the command line. Each transform can be applied to a patch only once.", transform);
@@ -2369,8 +2354,6 @@ namespace WixToolset.Data
2369 InvalidDateTimeFormat = 88, 2354 InvalidDateTimeFormat = 88,
2370 MultipleEntrySections = 89, 2355 MultipleEntrySections = 89,
2371 MultipleEntrySections2 = 90, 2356 MultipleEntrySections2 = 90,
2372 DuplicateSymbol = 91,
2373 DuplicateSymbol2 = 92,
2374 MissingEntrySection = 93, 2357 MissingEntrySection = 93,
2375 UnresolvedReference = 94, 2358 UnresolvedReference = 94,
2376 MultiplePrimaryReferences = 95, 2359 MultiplePrimaryReferences = 95,
diff --git a/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs b/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
index e614d4eb..f9d6ab59 100644
--- a/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
+++ b/src/wix/WixToolset.Core/Link/FindEntrySectionAndLoadSymbolsCommand.cs
@@ -36,7 +36,7 @@ namespace WixToolset.Core.Link
36 /// <summary> 36 /// <summary>
37 /// Gets the collection of possibly conflicting symbols. 37 /// Gets the collection of possibly conflicting symbols.
38 /// </summary> 38 /// </summary>
39 public IEnumerable<SymbolWithSection> PossibleConflicts { get; private set; } 39 public IReadOnlyCollection<SymbolWithSection> PossibleConflicts { get; private set; }
40 40
41 /// <summary> 41 /// <summary>
42 /// Gets the collection of redundant symbols that should not be included 42 /// Gets the collection of redundant symbols that should not be included
@@ -140,9 +140,7 @@ namespace WixToolset.Core.Link
140 { 140 {
141 if (symbolWithSection.Overrides is null) 141 if (symbolWithSection.Overrides is null)
142 { 142 {
143 var fullName = symbolWithSection.GetFullName(); 143 this.Messaging.Write(LinkerErrors.VirtualSymbolNotFoundForOverride(symbolWithSection.Symbol));
144
145 this.Messaging.Write(LinkerErrors.VirtualSymbolNotFoundForOverride(symbolWithSection.Symbol.SourceLineNumbers, fullName));
146 } 144 }
147 } 145 }
148 146
diff --git a/src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs b/src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
index 3a07d366..2851fa60 100644
--- a/src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
+++ b/src/wix/WixToolset.Core/Link/ReportConflictingSymbolsCommand.cs
@@ -9,7 +9,7 @@ namespace WixToolset.Core.Link
9 9
10 internal class ReportConflictingSymbolsCommand 10 internal class ReportConflictingSymbolsCommand
11 { 11 {
12 public ReportConflictingSymbolsCommand(IMessaging messaging, IEnumerable<SymbolWithSection> possibleConflicts, IEnumerable<IntermediateSection> resolvedSections) 12 public ReportConflictingSymbolsCommand(IMessaging messaging, IReadOnlyCollection<SymbolWithSection> possibleConflicts, ISet<IntermediateSection> resolvedSections)
13 { 13 {
14 this.Messaging = messaging; 14 this.Messaging = messaging;
15 this.PossibleConflicts = possibleConflicts; 15 this.PossibleConflicts = possibleConflicts;
@@ -18,35 +18,62 @@ namespace WixToolset.Core.Link
18 18
19 private IMessaging Messaging { get; } 19 private IMessaging Messaging { get; }
20 20
21 private IEnumerable<SymbolWithSection> PossibleConflicts { get; } 21 private IReadOnlyCollection<SymbolWithSection> PossibleConflicts { get; }
22 22
23 private IEnumerable<IntermediateSection> ResolvedSections { get; } 23 private ISet<IntermediateSection> ResolvedSections { get; }
24 24
25 public void Execute() 25 public void Execute()
26 { 26 {
27 // Do a quick check if there are any possibly conflicting symbols that don't come from tables that allow 27 // Do a quick check if there are any possibly conflicting symbols. Hopefully the symbols with possible conflicts
28 // overriding. Hopefully the symbols with possible conflicts list is usually very short list (empty should 28 // list is a very short list (empty should be the most common).
29 // be the most common). If we find any matches, we'll do a more costly check to see if the possible conflicting 29 //
30 // If we have conflicts then we'll do a more costly check to see if the possible conflicting
30 // symbols are in sections we actually referenced. From the resulting set, show an error for each duplicate 31 // symbols are in sections we actually referenced. From the resulting set, show an error for each duplicate
31 // (aka: conflicting) symbol. 32 // (aka: conflicting) symbol.
32 var illegalDuplicates = this.PossibleConflicts.Where(s => s.Symbol.Definition.Type != SymbolDefinitionType.WixAction && s.Symbol.Definition.Type != SymbolDefinitionType.WixVariable).ToList(); 33 if (0 < this.PossibleConflicts.Count)
33 if (0 < illegalDuplicates.Count)
34 { 34 {
35 var referencedSections = new HashSet<IntermediateSection>(this.ResolvedSections); 35 foreach (var referencedDuplicate in this.PossibleConflicts.Where(s => this.ResolvedSections.Contains(s.Section)))
36
37 foreach (var referencedDuplicate in illegalDuplicates.Where(s => referencedSections.Contains(s.Section)))
38 { 36 {
39 var actuallyReferencedDuplicates = referencedDuplicate.PossiblyConflicts.Where(s => referencedSections.Contains(s.Section)).ToList(); 37 var actuallyReferencedDuplicates = referencedDuplicate.PossiblyConflicts.Where(s => this.ResolvedSections.Contains(s.Section)).ToList();
40 38
41 if (actuallyReferencedDuplicates.Any()) 39 if (actuallyReferencedDuplicates.Count > 0)
42 { 40 {
43 var fullName = referencedDuplicate.GetFullName(); 41 var conflicts = actuallyReferencedDuplicates.Append(referencedDuplicate).ToList();
42 var virtualConflicts = conflicts.Where(s => s.Access == AccessModifier.Virtual).ToList();
43 var overrideConflicts = conflicts.Where(s => s.Access == AccessModifier.Override).ToList();
44 var otherConflicts = conflicts.Where(s => s.Access != AccessModifier.Virtual && s.Access != AccessModifier.Override).ToList();
45
46 IEnumerable<SymbolWithSection> reportDuplicates = actuallyReferencedDuplicates;
47
48 // If multiple symbols are virtual, use the duplicate virtual symbol message.
49 if (virtualConflicts.Count > 1)
50 {
51 var first = virtualConflicts[0];
52 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
44 53
45 this.Messaging.Write(ErrorMessages.DuplicateSymbol(referencedDuplicate.Symbol.SourceLineNumbers, fullName)); 54 reportDuplicates = virtualConflicts.Skip(1);
55
56 this.Messaging.Write(LinkerErrors.DuplicateVirtualSymbol(first.Symbol, referencingSourceLineNumber));
57 }
58 else if (virtualConflicts.Count == 1 && otherConflicts.Count > 0)
59 {
60 var first = otherConflicts[0];
61 var referencingSourceLineNumber = first.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
62
63 reportDuplicates = virtualConflicts;
64
65 this.Messaging.Write(LinkerErrors.VirtualSymbolMustBeOverridden(first.Symbol, referencingSourceLineNumber));
66 }
67 else
68 {
69 var referencingSourceLineNumber = referencedDuplicate.DirectReferences.FirstOrDefault()?.SourceLineNumbers;
70
71 this.Messaging.Write(LinkerErrors.DuplicateSymbol(referencedDuplicate.Symbol, referencingSourceLineNumber));
72 }
46 73
47 foreach (var duplicate in actuallyReferencedDuplicates) 74 foreach (var duplicate in reportDuplicates)
48 { 75 {
49 this.Messaging.Write(ErrorMessages.DuplicateSymbol2(duplicate.Symbol.SourceLineNumbers)); 76 this.Messaging.Write(LinkerErrors.DuplicateSymbol2(duplicate.Symbol));
50 } 77 }
51 } 78 }
52 } 79 }
diff --git a/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs b/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs
index 0edbf39c..0e7a7a52 100644
--- a/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs
+++ b/src/wix/WixToolset.Core/Link/ResolveReferencesCommand.cs
@@ -27,9 +27,9 @@ namespace WixToolset.Core.Link
27 this.BuildingMergeModule = (SectionType.Module == entrySection.Type); 27 this.BuildingMergeModule = (SectionType.Module == entrySection.Type);
28 } 28 }
29 29
30 public IEnumerable<SymbolWithSection> ReferencedSymbolWithSections => this.referencedSymbols; 30 public IReadOnlyCollection<SymbolWithSection> ReferencedSymbolWithSections => this.referencedSymbols;
31 31
32 public IEnumerable<IntermediateSection> ResolvedSections => this.resolvedSections; 32 public ISet<IntermediateSection> ResolvedSections => this.resolvedSections;
33 33
34 private bool BuildingMergeModule { get; } 34 private bool BuildingMergeModule { get; }
35 35
@@ -63,22 +63,25 @@ namespace WixToolset.Core.Link
63 // symbols provided. Then recursively call this method to process the 63 // symbols provided. Then recursively call this method to process the
64 // located symbol's section. All in all this is a very simple depth-first 64 // located symbol's section. All in all this is a very simple depth-first
65 // search of the references per-section. 65 // search of the references per-section.
66 foreach (var wixSimpleReferenceRow in section.Symbols.OfType<WixSimpleReferenceSymbol>()) 66 foreach (var reference in section.Symbols.OfType<WixSimpleReferenceSymbol>())
67 { 67 {
68 // If we're building a Merge Module, ignore all references to the Media table 68 // If we're building a Merge Module, ignore all references to the Media table
69 // because Merge Modules don't have Media tables. 69 // because Merge Modules don't have Media tables.
70 if (this.BuildingMergeModule && wixSimpleReferenceRow.Table == "Media") 70 if (this.BuildingMergeModule && reference.Table == "Media")
71 { 71 {
72 continue; 72 continue;
73 } 73 }
74 74
75 // See if the symbol (and any of its duplicates) are appropriately accessible. 75 // See if the symbol (and any of its duplicates) are appropriately accessible.
76 if (this.symbolsWithSections.TryGetValue(wixSimpleReferenceRow.SymbolicName, out var symbolWithSection)) 76 if (this.symbolsWithSections.TryGetValue(reference.SymbolicName, out var symbolWithSection))
77 { 77 {
78 var accessible = this.DetermineAccessibleSymbols(section, symbolWithSection); 78 var accessible = this.DetermineAccessibleSymbols(section, symbolWithSection);
79 if (accessible.Count == 1) 79 if (accessible.Count == 1)
80 { 80 {
81 var accessibleSymbol = accessible[0]; 81 var accessibleSymbol = accessible[0];
82
83 accessibleSymbol.AddDirectReference(reference);
84
82 if (this.referencedSymbols.Add(accessibleSymbol) && null != accessibleSymbol.Section) 85 if (this.referencedSymbols.Add(accessibleSymbol) && null != accessibleSymbol.Section)
83 { 86 {
84 this.RecursivelyResolveReferences(accessibleSymbol.Section); 87 this.RecursivelyResolveReferences(accessibleSymbol.Section);
@@ -86,32 +89,34 @@ namespace WixToolset.Core.Link
86 } 89 }
87 else if (accessible.Count == 0) 90 else if (accessible.Count == 0)
88 { 91 {
89 this.Messaging.Write(ErrorMessages.UnresolvedReference(wixSimpleReferenceRow.SourceLineNumbers, wixSimpleReferenceRow.SymbolicName, symbolWithSection.Access)); 92 this.Messaging.Write(ErrorMessages.UnresolvedReference(reference.SourceLineNumbers, reference.SymbolicName, symbolWithSection.Access));
90 } 93 }
91 else // display errors for the duplicate symbols. 94 else // multiple symbols referenced creates conflicting symbols.
92 { 95 {
93 var accessibleSymbol = accessible[0]; 96 // Remember the direct reference to the symbol for the error reporting later,
94 var accessibleFullName = accessibleSymbol.GetFullName(); 97 // but do NOT continue resolving references found in these conflicting symbols.
95 var referencingSourceLineNumber = wixSimpleReferenceRow.SourceLineNumbers?.ToString(); 98 foreach (var conflict in accessible)
96
97 if (String.IsNullOrEmpty(referencingSourceLineNumber))
98 {
99 this.Messaging.Write(ErrorMessages.DuplicateSymbol(accessibleSymbol.Symbol.SourceLineNumbers, accessibleFullName));
100 }
101 else
102 { 99 {
103 this.Messaging.Write(ErrorMessages.DuplicateSymbol(accessibleSymbol.Symbol.SourceLineNumbers, accessibleFullName, referencingSourceLineNumber)); 100 // This should NEVER happen.
104 } 101 if (!conflict.PossiblyConflicts.Any())
102 {
103 throw new InvalidOperationException("If a reference can reference multiple symbols, those symbols MUST have already been recognized as possible conflicts.");
104 }
105 105
106 foreach (var accessibleDuplicate in accessible.Skip(1)) 106 conflict.AddDirectReference(reference);
107 { 107
108 this.Messaging.Write(ErrorMessages.DuplicateSymbol2(accessibleDuplicate.Symbol.SourceLineNumbers)); 108 this.referencedSymbols.Add(conflict);
109
110 if (conflict.Section != null)
111 {
112 this.resolvedSections.Add(conflict.Section);
113 }
109 } 114 }
110 } 115 }
111 } 116 }
112 else 117 else
113 { 118 {
114 this.Messaging.Write(ErrorMessages.UnresolvedReference(wixSimpleReferenceRow.SourceLineNumbers, wixSimpleReferenceRow.SymbolicName)); 119 this.Messaging.Write(ErrorMessages.UnresolvedReference(reference.SourceLineNumbers, reference.SymbolicName));
115 } 120 }
116 } 121 }
117 } 122 }
@@ -133,14 +138,6 @@ namespace WixToolset.Core.Link
133 138
134 foreach (var dupe in symbolWithSection.PossiblyConflicts) 139 foreach (var dupe in symbolWithSection.PossiblyConflicts)
135 { 140 {
136 //// don't count overridable WixActionSymbols
137 //var symbolAction = symbolWithSection.Symbol as WixActionSymbol;
138 //var dupeAction = dupe.Symbol as WixActionSymbol;
139 //if (symbolAction?.Overridable != dupeAction?.Overridable)
140 //{
141 // continue;
142 //}
143
144 if (this.AccessibleSymbol(referencingSection, dupe)) 141 if (this.AccessibleSymbol(referencingSection, dupe))
145 { 142 {
146 accessibleSymbols.Add(dupe); 143 accessibleSymbols.Add(dupe);
diff --git a/src/wix/WixToolset.Core/Link/SymbolWithSection.cs b/src/wix/WixToolset.Core/Link/SymbolWithSection.cs
index 979aa44f..5bdf8360 100644
--- a/src/wix/WixToolset.Core/Link/SymbolWithSection.cs
+++ b/src/wix/WixToolset.Core/Link/SymbolWithSection.cs
@@ -6,12 +6,14 @@ namespace WixToolset.Core.Link
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Linq; 7 using System.Linq;
8 using WixToolset.Data; 8 using WixToolset.Data;
9 using WixToolset.Data.Symbols;
9 10
10 /// <summary> 11 /// <summary>
11 /// Symbol with section representing a single unique symbol. 12 /// Symbol with section representing a single unique symbol.
12 /// </summary> 13 /// </summary>
13 internal class SymbolWithSection 14 internal class SymbolWithSection
14 { 15 {
16 private List<WixSimpleReferenceSymbol> directReferences;
15 private HashSet<SymbolWithSection> possibleConflicts; 17 private HashSet<SymbolWithSection> possibleConflicts;
16 18
17 /// <summary> 19 /// <summary>
@@ -46,6 +48,11 @@ namespace WixToolset.Core.Link
46 /// <summary> 48 /// <summary>
47 /// Gets any duplicates of this symbol with sections that are possible conflicts. 49 /// Gets any duplicates of this symbol with sections that are possible conflicts.
48 /// </summary> 50 /// </summary>
51 public IEnumerable<WixSimpleReferenceSymbol> DirectReferences => this.directReferences ?? Enumerable.Empty<WixSimpleReferenceSymbol>();
52
53 /// <summary>
54 /// Gets any duplicates of this symbol with sections that are possible conflicts.
55 /// </summary>
49 public IEnumerable<SymbolWithSection> PossiblyConflicts => this.possibleConflicts ?? Enumerable.Empty<SymbolWithSection>(); 56 public IEnumerable<SymbolWithSection> PossiblyConflicts => this.possibleConflicts ?? Enumerable.Empty<SymbolWithSection>();
50 57
51 /// <summary> 58 /// <summary>
@@ -68,6 +75,20 @@ namespace WixToolset.Core.Link
68 } 75 }
69 76
70 /// <summary> 77 /// <summary>
78 /// Adds a reference that directly points to this symbol.
79 /// </summary>
80 /// <param name="reference">The direct reference.</param>
81 public void AddDirectReference(WixSimpleReferenceSymbol reference)
82 {
83 if (null == this.directReferences)
84 {
85 this.directReferences = new List<WixSimpleReferenceSymbol>();
86 }
87
88 this.directReferences.Add(reference);
89 }
90
91 /// <summary>
71 /// Override a virtual symbol. 92 /// Override a virtual symbol.
72 /// </summary> 93 /// </summary>
73 /// <param name="virtualSymbolWithSection">Virtual symbol with section that is being overridden.</param> 94 /// <param name="virtualSymbolWithSection">Virtual symbol with section that is being overridden.</param>
diff --git a/src/wix/WixToolset.Core/LinkerErrors.cs b/src/wix/WixToolset.Core/LinkerErrors.cs
index 78cd76f0..c234087e 100644
--- a/src/wix/WixToolset.Core/LinkerErrors.cs
+++ b/src/wix/WixToolset.Core/LinkerErrors.cs
@@ -11,6 +11,41 @@ namespace WixToolset.Core
11 return Message(null, Ids.DuplicateBindPathVariableOnCommandLine, "", argument, bindName, bindValue, collisionValue); 11 return Message(null, Ids.DuplicateBindPathVariableOnCommandLine, "", argument, bindName, bindValue, collisionValue);
12 } 12 }
13 13
14 public static Message DuplicateSymbol(IntermediateSymbol symbol)
15 {
16 return Message(symbol.SourceLineNumbers, Ids.DuplicateSymbol, "Duplicate {0} with identifier '{1}' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.", symbol.Definition.Name, symbol.Id.Id);
17 }
18
19 public static Message DuplicateSymbol(IntermediateSymbol symbol, SourceLineNumber referencingSourceLineNumber)
20 {
21 if (referencingSourceLineNumber is null)
22 {
23 return DuplicateSymbol(symbol);
24 }
25
26 return Message(symbol.SourceLineNumbers, Ids.DuplicateSymbol, "Duplicate {0} with identifier '{1}' referenced by {2}. Ensure all your identifiers of a given type (Directory, File, etc.) are unique or use an access modifier to scope the identfier.", symbol.Definition.Name, symbol.Id.Id, referencingSourceLineNumber);
27 }
28
29 public static Message DuplicateVirtualSymbol(IntermediateSymbol symbol)
30 {
31 return Message(symbol.SourceLineNumbers, Ids.DuplicateSymbol, "The virtual {0} with identifier '{1}' is duplicated. Ensure identifiers of a given type (Directory, File, etc.) are unique or did you mean to make one an override for the virtual symbol?", symbol.Definition.Name, symbol.Id.Id);
32 }
33
34 public static Message DuplicateVirtualSymbol(IntermediateSymbol symbol, SourceLineNumber referencingSourceLineNumber)
35 {
36 if (referencingSourceLineNumber is null)
37 {
38 return DuplicateVirtualSymbol(symbol);
39 }
40
41 return Message(symbol.SourceLineNumbers, Ids.DuplicateSymbol, "The virtual {0} with identifier '{1}' is duplicated. Ensure identifiers of a given type (Directory, File, etc.) are unique or did you mean to make one an override for the virtual symbol? Referenced from {2}", symbol.Definition.Name, symbol.Id.Id, referencingSourceLineNumber);
42 }
43
44 public static Message DuplicateSymbol2(IntermediateSymbol symbol)
45 {
46 return Message(symbol.SourceLineNumbers, Ids.DuplicateSymbol2, "Location of symbol related to previous error.");
47 }
48
14 public static Message OrphanedPayload(SourceLineNumber sourceLineNumbers, string payloadId) 49 public static Message OrphanedPayload(SourceLineNumber sourceLineNumbers, string payloadId)
15 { 50 {
16 return Message(sourceLineNumbers, Ids.OrphanedPayload, "Found orphaned Payload '{0}'. Make sure to reference it from a Package, the BootstrapperApplication, or the Bundle or move it into its own Fragment so it only gets linked in when actually used.", payloadId); 51 return Message(sourceLineNumbers, Ids.OrphanedPayload, "Found orphaned Payload '{0}'. Make sure to reference it from a Package, the BootstrapperApplication, or the Bundle or move it into its own Fragment so it only gets linked in when actually used.", payloadId);
@@ -46,9 +81,34 @@ namespace WixToolset.Core
46 return Message(sourceLineNumbers, Ids.UncompressedPayloadInContainer, "The payload '{0}' is uncompressed and cannot be added to container '{1}'. Remove its Compressed attribute and provide a @SourceFile value to allow it to be added to a container.", payloadId, containerId); 81 return Message(sourceLineNumbers, Ids.UncompressedPayloadInContainer, "The payload '{0}' is uncompressed and cannot be added to container '{1}'. Remove its Compressed attribute and provide a @SourceFile value to allow it to be added to a container.", payloadId, containerId);
47 } 82 }
48 83
49 public static Message VirtualSymbolNotFoundForOverride(SourceLineNumber sourceLineNumbers, string id) 84 public static Message VirtualSymbolNotFoundForOverride(IntermediateSymbol symbol)
85 {
86 return Message(symbol.SourceLineNumbers, Ids.VirtualSymbolNotFoundForOverride, "Could not find a virtual symbol to override with the {0} symbol '{1}'. Remove the override access modifier or include the code with the virtual symbol.", symbol.Definition.Name, symbol.Id.Id);
87 }
88
89 public static Message VirtualSymbolNotFoundForOverride(IntermediateSymbol symbol, SourceLineNumber referencingSourceLineNumber)
90 {
91 if (referencingSourceLineNumber is null)
92 {
93 return VirtualSymbolNotFoundForOverride(symbol);
94 }
95
96 return Message(symbol.SourceLineNumbers, Ids.VirtualSymbolNotFoundForOverride, "Could not find a virtual symbol to override with the {0} symbol '{1}'. Remove the override access modifier or include the code with the virtual symbol. Referenced from {2}", symbol.Definition.Name, symbol.Id.Id, referencingSourceLineNumber);
97 }
98
99 public static Message VirtualSymbolMustBeOverridden(IntermediateSymbol symbol)
50 { 100 {
51 return Message(sourceLineNumbers, Ids.VirtualSymbolNotFoundForOverride, "Did not find virtual symbol for override symbol '{0}'",id); 101 return Message(symbol.SourceLineNumbers, Ids.VirtualSymbolMustBeOverridden, "The {0} symbol '{1}' conflicts with a virtual symbol. Use the 'override' access modifier to override the virtual symbol or use a different Id to avoid the conflict.", symbol.Definition.Name, symbol.Id.Id);
102 }
103
104 public static Message VirtualSymbolMustBeOverridden(IntermediateSymbol symbol, SourceLineNumber referencingSourceLineNumber)
105 {
106 if (referencingSourceLineNumber is null)
107 {
108 return VirtualSymbolMustBeOverridden(symbol);
109 }
110
111 return Message(symbol.SourceLineNumbers, Ids.VirtualSymbolMustBeOverridden, "The {0} symbol '{1}' conflicts with a virtual symbol. Use the 'override' access modifier to override the virtual symbol or use a different Id to avoid the conflict. Referenced from {2}", symbol.Definition.Name, symbol.Id.Id, referencingSourceLineNumber);
52 } 112 }
53 113
54 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) 114 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
@@ -58,6 +118,9 @@ namespace WixToolset.Core
58 118
59 public enum Ids 119 public enum Ids
60 { 120 {
121 DuplicateSymbol = 91,
122 DuplicateSymbol2 = 92,
123
61 OrphanedPayload = 7000, 124 OrphanedPayload = 7000,
62 PackageInMultipleContainers = 7001, 125 PackageInMultipleContainers = 7001,
63 PayloadSharedWithBA = 7002, 126 PayloadSharedWithBA = 7002,
@@ -67,6 +130,7 @@ namespace WixToolset.Core
67 BAContainerCannotContainRemotePayload = 7006, 130 BAContainerCannotContainRemotePayload = 7006,
68 DuplicateBindPathVariableOnCommandLine = 7007, 131 DuplicateBindPathVariableOnCommandLine = 7007,
69 VirtualSymbolNotFoundForOverride = 7008, 132 VirtualSymbolNotFoundForOverride = 7008,
133 VirtualSymbolMustBeOverridden = 7009,
70 } // last available is 7099. 7100 is WindowsInstallerBackendWarnings. 134 } // last available is 7099. 7100 is WindowsInstallerBackendWarnings.
71 } 135 }
72} 136}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/AccessModifierFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/AccessModifierFixture.cs
index d0e31760..5e40114f 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/AccessModifierFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/AccessModifierFixture.cs
@@ -78,8 +78,8 @@ namespace WixToolsetTest.CoreIntegration
78 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicateCrossFragmentReference.wxs"); 78 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicateCrossFragmentReference.wxs");
79 WixAssert.CompareLineByLine(new[] 79 WixAssert.CompareLineByLine(new[]
80 { 80 {
81 @"ln 8: Duplicate symbol 'Directory:TestFolder' referenced by <sourceFolder>\DuplicateCrossFragmentReference.wxs(4). This typically means that an Id is duplicated. Ensure all your identifiers of a given type (Directory, File, etc.) are unique or use an access modifier to scope the identfier.", 81 "ln 12: Duplicate Directory with identifier 'TestFolder' referenced by <sourceFolder>\\DuplicateCrossFragmentReference.wxs(4). Ensure all your identifiers of a given type (Directory, File, etc.) are unique or use an access modifier to scope the identfier.",
82 "ln 12: Location of symbol related to previous error." 82 "ln 8: Location of symbol related to previous error."
83 }, errors); 83 }, errors);
84 } 84 }
85 85
@@ -89,7 +89,7 @@ namespace WixToolsetTest.CoreIntegration
89 var errors = BuildForFailure("TestData", "AccessModifier", "OverrideWithoutVirtualSymbol.wxs"); 89 var errors = BuildForFailure("TestData", "AccessModifier", "OverrideWithoutVirtualSymbol.wxs");
90 WixAssert.CompareLineByLine(new[] 90 WixAssert.CompareLineByLine(new[]
91 { 91 {
92 "ln 5: Did not find virtual symbol for override symbol 'Directory:TestFolder'", 92 "ln 5: Could not find a virtual symbol to override with the Directory symbol 'TestFolder'. Remove the override access modifier or include the code with the virtual symbol.",
93 }, errors); 93 }, errors);
94 } 94 }
95 95
@@ -99,7 +99,40 @@ namespace WixToolsetTest.CoreIntegration
99 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicatedOverrideVirtualSymbol.wxs"); 99 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicatedOverrideVirtualSymbol.wxs");
100 WixAssert.CompareLineByLine(new[] 100 WixAssert.CompareLineByLine(new[]
101 { 101 {
102 "ln 14: Duplicate symbol 'Directory:TestFolder' found. This typically means that an Id is duplicated. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.", 102 "ln 14: Duplicate Directory with identifier 'TestFolder' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.",
103 "ln 6: Location of symbol related to previous error."
104 }, errors);
105 }
106
107 [Fact]
108 public void CannotCompileDuplicatedVirtual()
109 {
110 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicatedVirtualSymbol.wxs");
111 WixAssert.CompareLineByLine(new[]
112 {
113 "ln 6: The virtual Directory with identifier 'TestFolder' is duplicated. Ensure identifiers of a given type (Directory, File, etc.) are unique or did you mean to make one an override for the virtual symbol?",
114 "ln 10: Location of symbol related to previous error."
115 }, errors);
116 }
117
118 [Fact]
119 public void CannotCompilePublicWithVirtualSymbol()
120 {
121 var errors = BuildForFailure("TestData", "AccessModifier", "VirtualSymbolWithoutOverride.wxs");
122 WixAssert.CompareLineByLine(new[]
123 {
124 "ln 9: The Directory symbol 'TestFolder' conflicts with a virtual symbol. Use the 'override' access modifier to override the virtual symbol or use a different Id to avoid the conflict.",
125 "ln 5: Location of symbol related to previous error."
126 }, errors);
127 }
128
129 [Fact]
130 public void CannotCompilePublicAndOverrideWithVirtualSymbol()
131 {
132 var errors = BuildForFailure("TestData", "AccessModifier", "DuplicatePublicOverrideVirtualSymbol.wxs");
133 WixAssert.CompareLineByLine(new[]
134 {
135 "ln 14: Duplicate Directory with identifier 'TestFolder' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.",
103 "ln 6: Location of symbol related to previous error." 136 "ln 6: Location of symbol related to previous error."
104 }, errors); 137 }, errors);
105 } 138 }
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/RegistryFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/RegistryFixture.cs
index 7bf10e3f..9931a45a 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/RegistryFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/RegistryFixture.cs
@@ -4,8 +4,8 @@ namespace WixToolsetTest.CoreIntegration
4{ 4{
5 using System.IO; 5 using System.IO;
6 using System.Linq; 6 using System.Linq;
7 using WixInternal.TestSupport;
8 using WixInternal.Core.TestPackage; 7 using WixInternal.Core.TestPackage;
8 using WixInternal.TestSupport;
9 using WixToolset.Data; 9 using WixToolset.Data;
10 using Xunit; 10 using Xunit;
11 11
@@ -97,8 +97,16 @@ namespace WixToolsetTest.CoreIntegration
97 "-o", msiPath 97 "-o", msiPath
98 }, out var messages); 98 }, out var messages);
99 99
100 Assert.Equal(2, messages.Where(m => m.Id == (int)ErrorMessages.Ids.DuplicateSymbol).Count()); 100 var errors = messages.Where(m => m.Level == MessageLevel.Error)
101 Assert.Equal(2, messages.Where(m => m.Id == (int)ErrorMessages.Ids.DuplicateSymbol2).Count()); 101 .Select(m => $"ln {m.SourceLineNumbers.LineNumber}: {m}".Replace(baseFolder, "<baseFolder>").Replace(folder, "<sourceFolder>"))
102 .ToArray();
103 WixAssert.CompareLineByLine(new[]
104 {
105 "ln 8: Duplicate Registry with identifier 'regJnkjRU9YGaMJhQOqKmivWKf_VdY' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.",
106 "ln 7: Location of symbol related to previous error.",
107 "ln 9: Duplicate Registry with identifier 'regJnkjRU9YGaMJhQOqKmivWKf_VdY' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.",
108 "ln 7: Location of symbol related to previous error."
109 }, errors);
102 } 110 }
103 } 111 }
104 112
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/RollbackBoundaryFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/RollbackBoundaryFixture.cs
index 8d8ac801..54375f67 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/RollbackBoundaryFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/RollbackBoundaryFixture.cs
@@ -66,7 +66,7 @@ namespace WixToolsetTest.CoreIntegration
66 66
67 WixAssert.CompareLineByLine(new[] 67 WixAssert.CompareLineByLine(new[]
68 { 68 {
69 "Duplicate symbol 'WixChainItem:collision' found. This typically means that an Id is duplicated. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.", 69 "Duplicate WixChainItem with identifier 'collision' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique.",
70 "Location of symbol related to previous error.", 70 "Location of symbol related to previous error.",
71 }, result.Messages.Select(m => m.ToString()).ToArray()); 71 }, result.Messages.Select(m => m.ToString()).ToArray());
72 72
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicatePublicOverrideVirtualSymbol.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicatePublicOverrideVirtualSymbol.wxs
new file mode 100644
index 00000000..7ecf4445
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicatePublicOverrideVirtualSymbol.wxs
@@ -0,0 +1,17 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 <Package Name="Duplicated Override Virtual Symbol" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
4
5 <StandardDirectory Id="ProgramFilesFolder">
6 <Directory Id="override TestFolder" Name="Override Test Folder" />
7 </StandardDirectory>
8
9 <StandardDirectory Id="ProgramFilesFolder">
10 <Directory Id="virtual TestFolder" Name="Test Folder" />
11 </StandardDirectory>
12
13 <StandardDirectory Id="ProgramFilesFolder">
14 <Directory Id="TestFolder" Name="Collision Test Folder" />
15 </StandardDirectory>
16 </Package>
17</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicatedVirtualSymbol.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicatedVirtualSymbol.wxs
new file mode 100644
index 00000000..e97d0b57
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/DuplicatedVirtualSymbol.wxs
@@ -0,0 +1,13 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 <Package Name="Duplicated Override Virtual Symbol" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
4
5 <StandardDirectory Id="ProgramFilesFolder">
6 <Directory Id="virtual TestFolder" Name="First Virtual Test Folder" />
7 </StandardDirectory>
8
9 <StandardDirectory Id="ProgramFilesFolder">
10 <Directory Id="virtual TestFolder" Name="Second Virtual Test Folder" />
11 </StandardDirectory>
12 </Package>
13</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/VirtualSymbolWithoutOverride.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/VirtualSymbolWithoutOverride.wxs
new file mode 100644
index 00000000..3db854f5
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AccessModifier/VirtualSymbolWithoutOverride.wxs
@@ -0,0 +1,12 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 <Package Name="Override Without Virtual Symbol" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
4 <StandardDirectory Id="ProgramFilesFolder">
5 <Directory Id="virtual TestFolder" Name="Test Folder" />
6 </StandardDirectory>
7
8 <StandardDirectory Id="ProgramFilesFolder">
9 <Directory Id="TestFolder" Name="Missing Override Access Modifier" />
10 </StandardDirectory>
11 </Package>
12</Wix>