-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expect mapped paths in InterceptsLocationAttribute #68242
Changes from 5 commits
8eb5ff6
6803108
ec5bd43
43ee0bb
21fc324
13b6964
88fc265
7c64b8a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2201,7 +2201,7 @@ internal enum ErrorCode | |
ERR_InterceptorLineOutOfRange = 27005, | ||
ERR_InterceptorCharacterOutOfRange = 27006, | ||
ERR_InterceptorSignatureMismatch = 27007, | ||
ERR_InterceptableMethodMustBeOrdinary = 27008, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
ERR_InterceptorPathNotInCompilationWithUnmappedCandidate = 27008, | ||
ERR_InterceptorMethodMustBeOrdinary = 27009, | ||
ERR_InterceptorMustReferToStartOfTokenPosition = 27010, | ||
ERR_InterceptorMustHaveMatchingThisParameter = 27011, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -967,8 +967,8 @@ private void DecodeInterceptsLocationAttribute(DecodeWellKnownAttributeArguments | |
return; | ||
} | ||
|
||
var filePath = (string?)attributeArguments[0].Value; | ||
if (filePath is null) | ||
var attributeFilePath = (string?)attributeArguments[0].Value; | ||
if (attributeFilePath is null) | ||
{ | ||
diagnostics.Add(ErrorCode.ERR_InterceptorFilePathCannotBeNull, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax)); | ||
return; | ||
|
@@ -987,42 +987,46 @@ private void DecodeInterceptsLocationAttribute(DecodeWellKnownAttributeArguments | |
} | ||
|
||
var syntaxTrees = DeclaringCompilation.SyntaxTrees; | ||
SyntaxTree? matchingTree = null; | ||
// PROTOTYPE(ic): we need to resolve the paths before comparing (i.e. respect /pathmap). | ||
// At that time, we should look at caching the resolved paths for the trees in a set (or maybe a Map<string, OneOrMany<SyntaxTree>>). | ||
// so we can reduce the cost of these checks. | ||
foreach (var tree in syntaxTrees) | ||
var matchingTrees = DeclaringCompilation.GetSyntaxTreesByMappedPath(attributeFilePath); | ||
if (matchingTrees.Count == 0) | ||
{ | ||
if (tree.FilePath == filePath) | ||
var referenceResolver = DeclaringCompilation.Options.SourceReferenceResolver; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: consider moving closer to usage #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because it's used in each subsequent branch in this block, I felt more comfortable declaring it where it is currently, instead of after the statement declaring I'm line-breaking some of the |
||
// if we expect '/_/Program.cs': | ||
|
||
// we might get: 'C:\Project\Program.cs' <-- path not mapped | ||
var unmappedMatch = syntaxTrees.FirstOrDefault(static (tree, filePath) => tree.FilePath == filePath, attributeFilePath); | ||
if (unmappedMatch != null) | ||
{ | ||
if (matchingTree == null) | ||
{ | ||
matchingTree = tree; | ||
// need to keep searching in case we find another tree with the same path | ||
} | ||
else | ||
{ | ||
diagnostics.Add(ErrorCode.ERR_InterceptorNonUniquePath, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax), filePath); | ||
return; | ||
} | ||
diagnostics.Add(ErrorCode.ERR_InterceptorPathNotInCompilationWithUnmappedCandidate, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax), attributeFilePath, mapPath(referenceResolver, unmappedMatch)); | ||
return; | ||
} | ||
} | ||
|
||
if (matchingTree == null) | ||
{ | ||
var suffixMatch = syntaxTrees.FirstOrDefault(static (tree, filePath) => tree.FilePath.EndsWith(filePath), filePath); | ||
// we might get: '\_\Program.cs' <-- slashes not normalized | ||
// we might get: '\_/Program.cs' <-- slashes don't match | ||
// we might get: 'Program.cs' <-- suffix match | ||
// Force normalization of all '\' to '/', but when we recommend a path in the diagnostic message, ensure it will match what we expect if the user decides to use it. | ||
var suffixMatch = syntaxTrees.FirstOrDefault(static (tree, pair) | ||
=> mapPath(pair.referenceResolver, tree) | ||
.Replace('\\', '/') | ||
.EndsWith(pair.attributeFilePath), | ||
(referenceResolver, attributeFilePath: attributeFilePath.Replace('\\', '/'))); | ||
if (suffixMatch != null) | ||
{ | ||
diagnostics.Add(ErrorCode.ERR_InterceptorPathNotInCompilationWithCandidate, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax), filePath, suffixMatch.FilePath); | ||
} | ||
else | ||
{ | ||
diagnostics.Add(ErrorCode.ERR_InterceptorPathNotInCompilation, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax), filePath); | ||
diagnostics.Add(ErrorCode.ERR_InterceptorPathNotInCompilationWithCandidate, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax), attributeFilePath, mapPath(referenceResolver, suffixMatch)); | ||
return; | ||
} | ||
|
||
diagnostics.Add(ErrorCode.ERR_InterceptorPathNotInCompilation, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax), attributeFilePath); | ||
|
||
return; | ||
} | ||
else if (matchingTrees.Count > 1) | ||
{ | ||
diagnostics.Add(ErrorCode.ERR_InterceptorNonUniquePath, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax), attributeFilePath); | ||
return; | ||
} | ||
|
||
SyntaxTree? matchingTree = matchingTrees[0]; | ||
// Internally, line and character numbers are 0-indexed, but when they appear in code or diagnostic messages, they are 1-indexed. | ||
int lineNumberZeroBased = lineNumberOneBased - 1; | ||
int characterNumberZeroBased = characterNumberOneBased - 1; | ||
|
@@ -1079,7 +1083,12 @@ private void DecodeInterceptsLocationAttribute(DecodeWellKnownAttributeArguments | |
return; | ||
} | ||
|
||
DeclaringCompilation.AddInterception(filePath, lineNumberZeroBased, characterNumberZeroBased, attributeLocation, this); | ||
DeclaringCompilation.AddInterception(matchingTree.FilePath, lineNumberZeroBased, characterNumberZeroBased, attributeLocation, this); | ||
|
||
static string mapPath(SourceReferenceResolver? referenceResolver, SyntaxTree tree) | ||
{ | ||
return referenceResolver?.NormalizePath(tree.FilePath, baseFilePath: null) ?? tree.FilePath; | ||
} | ||
} | ||
|
||
private void DecodeUnmanagedCallersOnlyAttribute(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments) | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that if this feature makes it past the experimental phase, we might revisit providing "convenience public APIs". For the current release, it feels more appropriate to indicate what helpers the generator author themselves needs to include.