[analysis_server] Add highlights/semantic tokens for augmented()

+ tests for fetching semantic tokens for macro-generated sources.

Change-Id: I1811e5fd3eb3f5564a1d6f21348f9650e30eb536
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368421
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
index 253d965..b444474 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -647,6 +647,20 @@
   }
 
   @override
+  void visitAugmentedExpression(AugmentedExpression node) {
+    computer._addRegion_token(
+        node.augmentedKeyword, HighlightRegionType.KEYWORD);
+    super.visitAugmentedExpression(node);
+  }
+
+  @override
+  void visitAugmentedInvocation(AugmentedInvocation node) {
+    computer._addRegion_token(
+        node.augmentedKeyword, HighlightRegionType.KEYWORD);
+    super.visitAugmentedInvocation(node);
+  }
+
+  @override
   void visitAwaitExpression(AwaitExpression node) {
     computer._addRegion_token(node.awaitKeyword, HighlightRegionType.BUILT_IN,
         semanticTokenModifiers: {CustomSemanticTokenModifiers.control});
diff --git a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
index cd9922f..11fe4ed 100644
--- a/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
+++ b/pkg/analysis_server/test/analysis/notification_highlights2_test.dart
@@ -1557,6 +1557,70 @@
     assertHasRegion(HighlightRegionType.KEYWORD, 'with A;');
   }
 
+  Future<void> test_KEYWORD_augmented_onInstanceGetter() async {
+    addTestFile('''
+class C {
+  augment int get g => augmented;
+}
+''');
+    await prepareHighlights();
+    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+  }
+
+  Future<void> test_KEYWORD_augmented_onInstanceMethod() async {
+    addTestFile('''
+class C {
+  augment int m() => augmented();
+}
+''');
+    await prepareHighlights();
+    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+  }
+
+  Future<void> test_KEYWORD_augmented_onInstanceSetter() async {
+    addTestFile('''
+class C {
+  augment set s(int x) { augmented = x; }
+}
+''');
+    await prepareHighlights();
+    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+  }
+
+  Future<void> test_KEYWORD_augmented_onStaticMethod() async {
+    addTestFile('''
+class C {
+  augment static int m() => augmented();
+}
+''');
+    await prepareHighlights();
+    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+  }
+
+  Future<void> test_KEYWORD_augmented_onTopLevelFunction() async {
+    addTestFile('''
+augment int f(int x) => augmented(x);
+''');
+    await prepareHighlights();
+    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+  }
+
+  Future<void> test_KEYWORD_augmented_onTopLevelGetter() async {
+    addTestFile('''
+augment int get g => augmented;
+''');
+    await prepareHighlights();
+    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+  }
+
+  Future<void> test_KEYWORD_augmented_onTopLevelSetter() async {
+    addTestFile('''
+augment set s(int x) { augmented = x; }
+''');
+    await prepareHighlights();
+    assertHasRegion(HighlightRegionType.KEYWORD, 'augmented');
+  }
+
   Future<void> test_KEYWORD_const_constructor() async {
     addTestFile('''
 class A {
diff --git a/pkg/analysis_server/test/lsp/augmentation_test.dart b/pkg/analysis_server/test/lsp/augmentation_test.dart
index b98e146..d412648 100644
--- a/pkg/analysis_server/test/lsp/augmentation_test.dart
+++ b/pkg/analysis_server/test/lsp/augmentation_test.dart
@@ -18,8 +18,6 @@
 
 @reflectiveTest
 class AugmentationTest extends AbstractLspAnalysisServerTest {
-  String get mainFileAugmentationPath => fromUri(mainFileAugmentationUri);
-
   Future<void> test_class_body_augmentationToAugmentation() async {
     await verifyGoToAugmentation('''
 class A {}
diff --git a/pkg/analysis_server/test/lsp/augmented_test.dart b/pkg/analysis_server/test/lsp/augmented_test.dart
index b40fce9..9eaa4a2 100644
--- a/pkg/analysis_server/test/lsp/augmented_test.dart
+++ b/pkg/analysis_server/test/lsp/augmented_test.dart
@@ -18,8 +18,6 @@
 
 @reflectiveTest
 class AugmentedTest extends AbstractLspAnalysisServerTest {
-  String get mainFileAugmentationPath => fromUri(mainFileAugmentationUri);
-
   Future<void> test_class_body_augmentationToAugmentation() async {
     await verifyGoToAugmented('''
 class A {}
diff --git a/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart b/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart
index e7a3657..1853251 100644
--- a/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart
+++ b/pkg/analysis_server/test/lsp/code_lens/augmentations_test.dart
@@ -39,8 +39,6 @@
   /// The title of the [Command] in the [CodeLens]es being tested.
   String get codeLensTitle;
 
-  String get mainFileAugmentationPath => fromUri(mainFileAugmentationUri);
-
   /// The range in [sourceUri] that the CodeLens should appear for.
   Range get sourceRange;
 
diff --git a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
index 5d10f34..438604f 100644
--- a/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
+++ b/pkg/analysis_server/test/lsp/semantic_tokens_test.dart
@@ -140,6 +140,87 @@
     );
   }
 
+  Future<void> test_augmentations() async {
+    var mainContent = '''
+import augment 'main_augmentation.dart';
+
+class A {
+  void f() {}
+  String get g => '';
+}
+''';
+
+    var augmentationContent = '''
+augment library 'main.dart';
+
+augment class A {
+  augment void f() {
+    augmented();
+  }
+  augment get g => augmented;
+}
+''';
+
+    newFile(mainFilePath, mainContent);
+    newFile(mainFileAugmentationPath, augmentationContent);
+    await initialize();
+
+    // Main library.
+    await _verifyTokens(mainFileUri, mainContent, [
+      _Token('import', SemanticTokenTypes.keyword),
+      _Token('augment', SemanticTokenTypes.keyword),
+      _Token("'main_augmentation.dart'", SemanticTokenTypes.string),
+      _Token('class', SemanticTokenTypes.keyword),
+      _Token(
+        'A',
+        SemanticTokenTypes.class_,
+        [SemanticTokenModifiers.declaration],
+      ),
+      _Token(
+        'void',
+        SemanticTokenTypes.keyword,
+        [CustomSemanticTokenModifiers.void_],
+      ),
+      _Token('f', SemanticTokenTypes.method, [
+        SemanticTokenModifiers.declaration,
+        CustomSemanticTokenModifiers.instance
+      ]),
+      _Token('String', SemanticTokenTypes.class_),
+      _Token('get', SemanticTokenTypes.keyword),
+      _Token('g', SemanticTokenTypes.property, [
+        SemanticTokenModifiers.declaration,
+        CustomSemanticTokenModifiers.instance
+      ]),
+      _Token("''", SemanticTokenTypes.string),
+    ]);
+
+    // Augmentation.
+    await _verifyTokens(mainFileAugmentationUri, augmentationContent, [
+      _Token('augment', SemanticTokenTypes.keyword),
+      _Token('library', SemanticTokenTypes.keyword),
+      _Token("'main.dart'", SemanticTokenTypes.string),
+      _Token('augment', SemanticTokenTypes.keyword),
+      _Token('class', SemanticTokenTypes.keyword),
+      _Token(
+          'A', SemanticTokenTypes.class_, [SemanticTokenModifiers.declaration]),
+      _Token('augment', SemanticTokenTypes.keyword),
+      _Token('void', SemanticTokenTypes.keyword,
+          [CustomSemanticTokenModifiers.void_]),
+      _Token('f', SemanticTokenTypes.method, [
+        SemanticTokenModifiers.declaration,
+        CustomSemanticTokenModifiers.instance
+      ]),
+      _Token('augmented', SemanticTokenTypes.keyword),
+      _Token('augment', SemanticTokenTypes.keyword),
+      _Token('get', SemanticTokenTypes.keyword),
+      _Token('g', SemanticTokenTypes.property, [
+        SemanticTokenModifiers.declaration,
+        CustomSemanticTokenModifiers.instance
+      ]),
+      _Token('augmented', SemanticTokenTypes.keyword),
+    ]);
+  }
+
   Future<void> test_class() async {
     var content = '''
 /// class docs
@@ -161,7 +242,7 @@
       _Token('// Trailing comment', SemanticTokenTypes.comment),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_class_constructors() async {
@@ -240,7 +321,7 @@
           [CustomSemanticTokenModifiers.constructor]),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_class_fields() async {
@@ -297,7 +378,7 @@
       _Token("'a'", SemanticTokenTypes.string),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_class_getterSetter() async {
@@ -379,7 +460,7 @@
       _Token("'a'", SemanticTokenTypes.string),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_class_method() async {
@@ -455,7 +536,7 @@
           [SemanticTokenModifiers.static]),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_class_super() async {
@@ -506,7 +587,7 @@
       ])
     ];
 
-    await _verifyTokensInRange(content, expected);
+    await _initializeAndVerifyTokensInRange(content, expected);
   }
 
   Future<void> test_class_this() async {
@@ -554,7 +635,7 @@
       ])
     ];
 
-    await _verifyTokensInRange(content, expected);
+    await _initializeAndVerifyTokensInRange(content, expected);
   }
 
   Future<void> test_dartdoc() async {
@@ -598,7 +679,7 @@
       _Token('2', SemanticTokenTypes.number)
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_directives() async {
@@ -637,7 +718,7 @@
       _Token("'file_html.dart'", SemanticTokenTypes.string),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_extension() async {
@@ -652,7 +733,7 @@
       _Token('String', SemanticTokenTypes.class_)
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_extensionType() async {
@@ -670,7 +751,7 @@
           [SemanticTokenModifiers.declaration])
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_fromPlugin() async {
@@ -814,7 +895,7 @@
       _Token('isEven', SemanticTokenTypes.variable),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_lastLine_code() async {
@@ -826,7 +907,7 @@
           [SemanticTokenModifiers.declaration]),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_lastLine_comment() async {
@@ -836,7 +917,7 @@
       _Token('// Trailing comment', SemanticTokenTypes.comment),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_lastLine_multilineComment() async {
@@ -854,7 +935,7 @@
           [SemanticTokenModifiers.documentation]),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_local() async {
@@ -883,7 +964,61 @@
       _Token('func', SemanticTokenTypes.function),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
+  }
+
+  /// Verify we can send requests for semantic tokens inside files generated
+  /// by macros (which are not file:/// scheme).
+  Future<void> test_macroGenerated() async {
+    setDartTextDocumentContentProviderSupport();
+    addMacros([declareInTypeMacro()]);
+
+    const mainContent = '''
+import 'macros.dart';
+
+@DeclareInType('void f() {}')
+class A {}
+''';
+
+    // Create the file and start up the server so that the macro-generated
+    // files is available.
+    newFile(mainFilePath, mainContent);
+    await Future.wait([
+      waitForAnalysisComplete(),
+      initialize(),
+    ]);
+
+    // Fetch the macro-generated content to ensure it was generated successfully
+    // but also because verifyTokens uses the content to map locations back to
+    // source code to simplify comparing the tokens.
+    var generatedFile = await getDartTextDocumentContent(mainFileMacroUri);
+    var generatedContent = generatedFile!.content!;
+
+    await _verifyTokens(mainFileMacroUri, generatedContent, [
+      _Token('augment', SemanticTokenTypes.keyword),
+      _Token('library', SemanticTokenTypes.keyword),
+      _Token("'package:test/main.dart'", SemanticTokenTypes.string),
+      _Token('augment', SemanticTokenTypes.keyword),
+      _Token('class', SemanticTokenTypes.keyword),
+      _Token(
+        'A',
+        SemanticTokenTypes.class_,
+        [SemanticTokenModifiers.declaration],
+      ),
+      _Token(
+        'void',
+        SemanticTokenTypes.keyword,
+        [CustomSemanticTokenModifiers.void_],
+      ),
+      _Token(
+        'f',
+        SemanticTokenTypes.method,
+        [
+          SemanticTokenModifiers.declaration,
+          CustomSemanticTokenModifiers.instance
+        ],
+      )
+    ]);
   }
 
   Future<void> test_manyBools_bug() async {
@@ -936,7 +1071,7 @@
       ],
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_manyImports_sortBug() async {
@@ -969,7 +1104,7 @@
       ],
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_mixin() async {
@@ -988,7 +1123,7 @@
           'C', SemanticTokenTypes.class_, [SemanticTokenModifiers.declaration])
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_multilineRegions() async {
@@ -1020,7 +1155,7 @@
           [SemanticTokenModifiers.declaration]),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_namedArguments() async {
@@ -1042,7 +1177,7 @@
       _Token('a', SemanticTokenTypes.parameter),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_never() async {
@@ -1066,7 +1201,7 @@
       _Token("''", SemanticTokenTypes.string),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_patterns_assignment() async {
@@ -1102,7 +1237,7 @@
       _Token('2', SemanticTokenTypes.number)
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_patterns_switch_list() async {
@@ -1134,7 +1269,7 @@
       _Token('null', SemanticTokenTypes.keyword)
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_patterns_switch_object() async {
@@ -1167,7 +1302,7 @@
       _Token('isEven', SemanticTokenTypes.variable),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_patterns_switch_object_inferredName() async {
@@ -1198,7 +1333,7 @@
       _Token('isEven', SemanticTokenTypes.variable),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_range() async {
@@ -1218,7 +1353,7 @@
       _Token('// class comment', SemanticTokenTypes.comment),
     ];
 
-    await _verifyTokensInRange(content, expected);
+    await _initializeAndVerifyTokensInRange(content, expected);
   }
 
   Future<void> test_range_entireFile() async {
@@ -1242,7 +1377,7 @@
       _Token('// Trailing comment', SemanticTokenTypes.comment),
     ];
 
-    await _verifyTokensInRange(content, expected);
+    await _initializeAndVerifyTokensInRange(content, expected);
   }
 
   Future<void> test_range_multilineRegions() async {
@@ -1266,7 +1401,7 @@
       _Token('class', SemanticTokenTypes.keyword),
     ];
 
-    await _verifyTokensInRange(content, expected);
+    await _initializeAndVerifyTokensInRange(content, expected);
   }
 
   Future<void> test_record_fields() async {
@@ -1302,7 +1437,7 @@
       _Token('unresolved', CustomSemanticTokenTypes.source),
     ];
 
-    await _verifyTokensInRange(content, expected);
+    await _initializeAndVerifyTokensInRange(content, expected);
   }
 
   Future<void> test_sort_sameOffsets() async {
@@ -1327,7 +1462,7 @@
       _Token("'", SemanticTokenTypes.string)
     ];
 
-    await _verifyTokensInRange(content, expected);
+    await _initializeAndVerifyTokensInRange(content, expected);
   }
 
   Future<void> test_strings() async {
@@ -1394,7 +1529,7 @@
       _Token("'''", SemanticTokenTypes.string),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_strings_escape() async {
@@ -1447,7 +1582,7 @@
       _Token(r"\u{12345699}'", SemanticTokenTypes.string),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_topLevel() async {
@@ -1512,7 +1647,7 @@
       _Token('funcTearOff', SemanticTokenTypes.property),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   Future<void> test_unresolvedOrInvalid() async {
@@ -1553,7 +1688,7 @@
       _Token('baz', CustomSemanticTokenTypes.source),
     ];
 
-    await _verifyTokens(content, expected);
+    await _initializeAndVerifyTokens(content, expected);
   }
 
   /// Decode tokens according to the LSP spec and pair with relevant file contents.
@@ -1588,26 +1723,52 @@
     return results;
   }
 
-  Future<void> _verifyTokens(String content, List<_Token> expected) async {
+  /// Initializes the server with [content] in [uri] and then calls
+  /// [_verifyTokens] to check the semantic tokens match [expected].
+  Future<void> _initializeAndVerifyTokens(
+    String content,
+    List<_Token> expected, {
+    Uri? uri,
+  }) async {
+    uri ??= mainFileUri;
     var code = TestCode.parse(content);
+    newFile(fromUri(uri), code.code);
     await initialize();
-    await openFile(mainFileUri, code.code);
 
-    var tokens = await getSemanticTokens(mainFileUri);
-    var decoded = _decodeSemanticTokens(content, tokens);
-    expect(decoded, equals(expected));
+    await _verifyTokens(uri, content, expected);
   }
 
-  Future<void> _verifyTokensInRange(
-      String content, List<_Token> expected) async {
+  /// Initializes the server with [content] in [uri] and then checks the
+  ///  semantic tokens for the marked range match [expected].
+  Future<void> _initializeAndVerifyTokensInRange(
+    String content,
+    List<_Token> expected, {
+    Uri? uri,
+  }) async {
+    uri ??= mainFileUri;
     var code = TestCode.parse(content);
+    newFile(fromUri(uri), code.code);
     await initialize();
-    await openFile(mainFileUri, code.code);
 
     var tokens = await getSemanticTokensRange(mainFileUri, code.range.range);
     var decoded = _decodeSemanticTokens(code.code, tokens);
     expect(decoded, equals(expected));
   }
+
+  /// Check the semantic tokens for [content] in [uri] match [expected].
+  ///
+  /// [content] is used to map the offsets in the response to the tokens and
+  /// is not sent to the server, so it must already match what the server
+  /// believes [uri] to contain.
+  Future<void> _verifyTokens(
+    Uri uri,
+    String content,
+    List<_Token> expected,
+  ) async {
+    var tokens = await getSemanticTokens(uri);
+    var decoded = _decodeSemanticTokens(content, tokens);
+    expect(decoded, equals(expected));
+  }
 }
 
 class _Token {
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index 71063ab..3175d62 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -67,6 +67,8 @@
 
   DartFixPromptManager? get dartFixPromptManager => null;
 
+  String get mainFileAugmentationPath => fromUri(mainFileAugmentationUri);
+
   /// The path that is not in [projectFolderPath], contains external packages.
   @override
   String get packagesRootPath => resourceProvider.convertPath('/packages');