Skip to content
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

Interceptors: handle duplicates, additional signature validation, etc. #67786

Merged
merged 34 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5c4e289
Detect duplicate interceptions
RikkiGibson Apr 13, 2023
b769c0e
fix formatting
RikkiGibson Apr 13, 2023
a1f0eab
Fix dictionary usage. Test some file local attribute cases. Check sco…
RikkiGibson Apr 15, 2023
042d1d6
Fix nullable warning
RikkiGibson Apr 17, 2023
565c39a
Test 'params'. Test 'object.ReferenceEquals'. Handle more prototype c…
RikkiGibson Apr 17, 2023
f09ecb9
fix position diagnostics
RikkiGibson Apr 17, 2023
5edf9cf
fix lsp test
RikkiGibson Apr 18, 2023
b5fb444
Don't propagate InvokedAsExtensionMethod past lowering
RikkiGibson Apr 18, 2023
732dd91
Test file path case difference
RikkiGibson Apr 18, 2023
e47ce9d
Permit safe nullable differences
RikkiGibson Apr 19, 2023
678c4aa
Fix DiagnosticTest
RikkiGibson Apr 19, 2023
330ae61
Update spec
RikkiGibson Apr 19, 2023
aec866a
fix example type
RikkiGibson Apr 19, 2023
4b7fa44
Use explicit 'bool' type
RikkiGibson Apr 19, 2023
3534505
remove TODO
RikkiGibson Apr 19, 2023
d2053de
Fix crash when ROSLYN_TEST_USEDASSEMBLIES is defined
RikkiGibson Apr 19, 2023
d47412a
Fix nullable warning
RikkiGibson Apr 19, 2023
ca4cea8
Fix formatting
RikkiGibson Apr 19, 2023
21c1d56
More tests
RikkiGibson Apr 19, 2023
805fc9a
Add more tests
RikkiGibson Apr 20, 2023
87bcad6
Update baselines. Prefer reporting mismatch issues on attribute inste…
RikkiGibson Apr 20, 2023
3da2bad
Handle concurrent attribute decoding
RikkiGibson Apr 20, 2023
f4f05aa
add PROTOTYPE
RikkiGibson Apr 20, 2023
202914a
permit safe scoped variance
RikkiGibson Apr 20, 2023
c677ca7
Execute more test. Test intercepting ReferenceEquals.
RikkiGibson Apr 20, 2023
3a95687
Adjust comment
RikkiGibson Apr 20, 2023
2bfe8e2
adjust comment
RikkiGibson Apr 20, 2023
03be70a
skip verification in InterceptorExtern
RikkiGibson Apr 20, 2023
5c97905
add another duplicate test
RikkiGibson Apr 21, 2023
b6eb568
Clarify comment
RikkiGibson Apr 21, 2023
76d66d9
Add use site info
RikkiGibson Apr 21, 2023
f3c877e
fix double negative
RikkiGibson Apr 21, 2023
43205e8
test interceptable call with method type arguments in signature
RikkiGibson Apr 21, 2023
8664dad
add PROTOTYPE based on offline discussion
RikkiGibson Apr 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix position diagnostics
  • Loading branch information
RikkiGibson committed Apr 17, 2023
commit f09ecb9277e266f55b6d8591758a0fd6e2a3fa6c
5 changes: 4 additions & 1 deletion src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -7523,11 +7523,14 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_InterceptorCharacterOutOfRange" xml:space="preserve">
<value>The given line is '{0}' characters long, which is fewer than the provided character number '{1}'.</value>
</data>
<data name="ERR_InterceptorLineCharacterMustBePositive" xml:space="preserve">
<value>Line and character numbers provided to InterceptsLocationAttribute must be positive.</value>
</data>
<data name="ERR_InterceptorPositionBadToken" xml:space="preserve">
<value>The provided line and character number does not refer to an interceptable method name, but rather to token '{0}'.</value>
</data>
<data name="ERR_InterceptorMustReferToStartOfTokenPosition" xml:space="preserve">
<value>The provided character number does not refer to the start of method name token '{0}'. Consider using character number '{1}' instead.</value>
<value>The provided line and character number does not refer to the start of token '{0}'. Did you mean to use line '{1}' and character '{2}'?</value>
</data>
<data name="ERR_InterceptorSignatureMismatch" xml:space="preserve">
<value>Cannot intercept method '{0}' with interceptor '{1}' because the signatures do not match.</value>
Expand Down
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2213,6 +2213,7 @@ internal enum ErrorCode
WRN_InterceptorSignatureMismatch = 27017,
ERR_InterceptorNotAccessible = 27018,
ERR_InterceptorScopedMismatch = 27019,
ERR_InterceptorLineCharacterMustBePositive = 27020,

#endregion

Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code)
case ErrorCode.ERR_InterceptorMustNotHaveThisParameter:
case ErrorCode.ERR_DuplicateInterceptor:
case ErrorCode.WRN_InterceptorSignatureMismatch:
case ErrorCode.ERR_InterceptorNotAccessible:
case ErrorCode.ERR_InterceptorScopedMismatch:
// Update src\EditorFeatures\CSharp\LanguageServer\CSharpLspBuildOnlyDiagnostics.cs
// whenever new values are added here.
Expand Down Expand Up @@ -2330,6 +2331,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code)
case ErrorCode.ERR_InterceptorFilePathCannotBeNull:
case ErrorCode.ERR_InterceptorNameNotInvoked:
case ErrorCode.ERR_InterceptorNonUniquePath:
case ErrorCode.ERR_InterceptorLineCharacterMustBePositive:
case ErrorCode.ERR_ConstantValueOfTypeExpected:
case ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRefAny:
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -950,7 +950,8 @@ private void DecodeInterceptsLocationAttribute(DecodeWellKnownAttributeArguments
{
Debug.Assert(arguments.AttributeSyntaxOpt is object);
Debug.Assert(!arguments.Attribute.HasErrors);
var attributeArguments = arguments.Attribute.CommonConstructorArguments;
var attributeData = arguments.Attribute;
var attributeArguments = attributeData.CommonConstructorArguments;
if (attributeArguments is not [
{ Type.SpecialType: SpecialType.System_String },
{ Kind: not TypedConstantKind.Array, Value: int lineNumberOneBased },
Expand All @@ -961,12 +962,16 @@ private void DecodeInterceptsLocationAttribute(DecodeWellKnownAttributeArguments
}

var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics;
var attributeLocation = arguments.AttributeSyntaxOpt.Location;
var attributeSyntax = arguments.AttributeSyntaxOpt;
var attributeLocation = attributeSyntax.Location;
const int filePathParameterIndex = 0;
const int lineNumberParameterIndex = 1;
const int characterNumberParameterIndex = 2;

var filePath = (string?)attributeArguments[0].Value;
if (filePath is null)
{
diagnostics.Add(ErrorCode.ERR_InterceptorFilePathCannotBeNull, attributeLocation);
diagnostics.Add(ErrorCode.ERR_InterceptorFilePathCannotBeNull, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax));
return;
}

Expand All @@ -983,46 +988,54 @@ private void DecodeInterceptsLocationAttribute(DecodeWellKnownAttributeArguments
}

var syntaxTrees = DeclaringCompilation.SyntaxTrees;
// PROTOTYPE(ic): consider avoiding an array allocation here, on the assumption that 1 matching tree is the success (common) case, 0 is the most common error case, and 2 or more is much more rare.
var matchingTrees = syntaxTrees.WhereAsArray(static (tree, filePath) => tree.FilePath == filePath, filePath);
if (matchingTrees is [])
{
var suffixMatch = syntaxTrees.FirstOrDefault(static (tree, filePath) => tree.FilePath.EndsWith(filePath), filePath);
if (suffixMatch != null)
{
diagnostics.Add(ErrorCode.ERR_InterceptorPathNotInCompilationWithCandidate, attributeLocation, filePath, suffixMatch.FilePath);
diagnostics.Add(ErrorCode.ERR_InterceptorPathNotInCompilationWithCandidate, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax), filePath, suffixMatch.FilePath);
}
else
{
diagnostics.Add(ErrorCode.ERR_InterceptorPathNotInCompilation, attributeLocation, filePath);
diagnostics.Add(ErrorCode.ERR_InterceptorPathNotInCompilation, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax), filePath);
}

return;
}

if (matchingTrees is not [var matchingTree])
{
diagnostics.Add(ErrorCode.ERR_InterceptorNonUniquePath, attributeLocation, filePath);
diagnostics.Add(ErrorCode.ERR_InterceptorNonUniquePath, attributeData.GetAttributeArgumentSyntaxLocation(filePathParameterIndex, attributeSyntax), filePath);
return;
}

// Internally, line and character numbers are 0-indexed, but when they appear in code or diagnostic messages, they are 1-indexed.
// PROTOTYPE(ic): test with zero or negative display line/character numbers.
int lineNumberZeroBased = lineNumberOneBased - 1;
int characterNumberZeroBased = characterNumberOneBased - 1;

if (lineNumberZeroBased < 0 || characterNumberZeroBased < 0)
{
var location = attributeData.GetAttributeArgumentSyntaxLocation(lineNumberZeroBased < 0 ? lineNumberParameterIndex : characterNumberParameterIndex, attributeSyntax);
diagnostics.Add(ErrorCode.ERR_InterceptorLineCharacterMustBePositive, location);
return;
}

var referencedLines = matchingTree.GetText().Lines;
var referencedLineCount = referencedLines.Count;

if (lineNumberZeroBased >= referencedLineCount)
{
diagnostics.Add(ErrorCode.ERR_InterceptorLineOutOfRange, attributeLocation, referencedLineCount, lineNumberOneBased);
diagnostics.Add(ErrorCode.ERR_InterceptorLineOutOfRange, attributeData.GetAttributeArgumentSyntaxLocation(lineNumberParameterIndex, attributeSyntax), referencedLineCount, lineNumberOneBased);
return;
}

var line = referencedLines[lineNumberZeroBased];
var lineLength = line.End - line.Start;
if (characterNumberZeroBased >= lineLength)
{
diagnostics.Add(ErrorCode.ERR_InterceptorCharacterOutOfRange, attributeLocation, lineLength, characterNumberOneBased);
diagnostics.Add(ErrorCode.ERR_InterceptorCharacterOutOfRange, attributeData.GetAttributeArgumentSyntaxLocation(characterNumberParameterIndex, attributeSyntax), lineLength, characterNumberOneBased);
return;
}

Expand All @@ -1046,12 +1059,11 @@ private void DecodeInterceptsLocationAttribute(DecodeWellKnownAttributeArguments
return;
}

// Did they actually refer to the start of the token, not the middle, or in leading trivia?
var tokenPositionDifference = referencedPosition - referencedToken.Span.Start;
if (tokenPositionDifference != 0)
// Did they actually refer to the start of the token, not the middle, or in trivia?
if (referencedPosition != referencedToken.Span.Start)
{
// PROTOTYPE(ic): when a token's leading trivia spans multiple lines, this doesn't suggest a valid position.
diagnostics.Add(ErrorCode.ERR_InterceptorMustReferToStartOfTokenPosition, attributeLocation, referencedToken.Text, characterNumberOneBased - tokenPositionDifference);
var linePositionZeroBased = referencedToken.GetLocation().GetLineSpan().StartLinePosition;
diagnostics.Add(ErrorCode.ERR_InterceptorMustReferToStartOfTokenPosition, attributeLocation, referencedToken.Text, linePositionZeroBased.Line + 1, linePositionZeroBased.Character + 1);
return;
}

Expand Down
9 changes: 7 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading